xref: /petsc/src/dm/impls/plex/plex.c (revision 750b007cd8d816cecd9de99077bb0a703b4cf61a)
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   case DM_POLYTOPE_FV_GHOST: break;
1606   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1607   }
1608   PetscFunctionReturn(0);
1609 }
1610 
1611 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[]) {
1612   DMPolytopeType ct;
1613   PetscReal      centroid[2] = {0., 0.};
1614   PetscMPIInt    rank;
1615   PetscInt       fillColor, v, e, d;
1616 
1617   PetscFunctionBegin;
1618   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1619   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1620   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1621   switch (ct) {
1622   case DM_POLYTOPE_TRIANGLE: {
1623     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1624 
1625     for (v = 0; v < 3; ++v) {
1626       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1627       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1628     }
1629     for (e = 0; e < 3; ++e) {
1630       refCoords[0] = refVertices[e * 2 + 0];
1631       refCoords[1] = refVertices[e * 2 + 1];
1632       for (d = 1; d <= edgeDiv; ++d) {
1633         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1634         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1635       }
1636       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1637       for (d = 0; d < edgeDiv; ++d) {
1638         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));
1639         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1640       }
1641     }
1642   } break;
1643   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1644   }
1645   PetscFunctionReturn(0);
1646 }
1647 
1648 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer) {
1649   PetscDraw          draw;
1650   DM                 cdm;
1651   PetscSection       coordSection;
1652   Vec                coordinates;
1653   const PetscScalar *coords;
1654   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1655   PetscReal         *refCoords, *edgeCoords;
1656   PetscBool          isnull, drawAffine = PETSC_TRUE;
1657   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1658 
1659   PetscFunctionBegin;
1660   PetscCall(DMGetCoordinateDim(dm, &dim));
1661   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1662   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1663   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1664   PetscCall(DMGetCoordinateDM(dm, &cdm));
1665   PetscCall(DMGetLocalSection(cdm, &coordSection));
1666   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1667   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1668   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1669 
1670   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1671   PetscCall(PetscDrawIsNull(draw, &isnull));
1672   if (isnull) PetscFunctionReturn(0);
1673   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1674 
1675   PetscCall(VecGetLocalSize(coordinates, &N));
1676   PetscCall(VecGetArrayRead(coordinates, &coords));
1677   for (c = 0; c < N; c += dim) {
1678     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1679     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1680     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1681     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1682   }
1683   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1684   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1685   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1686   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1687   PetscCall(PetscDrawClear(draw));
1688 
1689   for (c = cStart; c < cEnd; ++c) {
1690     PetscScalar *coords = NULL;
1691     PetscInt     numCoords;
1692 
1693     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1694     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1695     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1696     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1697   }
1698   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1699   PetscCall(PetscDrawFlush(draw));
1700   PetscCall(PetscDrawPause(draw));
1701   PetscCall(PetscDrawSave(draw));
1702   PetscFunctionReturn(0);
1703 }
1704 
1705 #if defined(PETSC_HAVE_EXODUSII)
1706 #include <exodusII.h>
1707 #include <petscviewerexodusii.h>
1708 #endif
1709 
1710 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer) {
1711   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1712   char      name[PETSC_MAX_PATH_LEN];
1713 
1714   PetscFunctionBegin;
1715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1716   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1723   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1724   if (iascii) {
1725     PetscViewerFormat format;
1726     PetscCall(PetscViewerGetFormat(viewer, &format));
1727     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1728     else PetscCall(DMPlexView_Ascii(dm, viewer));
1729   } else if (ishdf5) {
1730 #if defined(PETSC_HAVE_HDF5)
1731     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1732 #else
1733     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1734 #endif
1735   } else if (isvtk) {
1736     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1737   } else if (isdraw) {
1738     PetscCall(DMPlexView_Draw(dm, viewer));
1739   } else if (isglvis) {
1740     PetscCall(DMPlexView_GLVis(dm, viewer));
1741 #if defined(PETSC_HAVE_EXODUSII)
1742   } else if (isexodus) {
1743     /*
1744       exodusII requires that all sets be part of exactly one cell set.
1745       If the dm does not have a "Cell Sets" label defined, we create one
1746       with ID 1, containig all cells.
1747       Note that if the Cell Sets label is defined but does not cover all cells,
1748       we may still have a problem. This should probably be checked here or in the viewer;
1749     */
1750     PetscInt numCS;
1751     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1752     if (!numCS) {
1753       PetscInt cStart, cEnd, c;
1754       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1755       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1756       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1757     }
1758     PetscCall(DMView_PlexExodusII(dm, viewer));
1759 #endif
1760 #if defined(PETSC_HAVE_CGNS)
1761   } else if (iscgns) {
1762     PetscCall(DMView_PlexCGNS(dm, viewer));
1763 #endif
1764   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1765   /* Optionally view the partition */
1766   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1767   if (flg) {
1768     Vec ranks;
1769     PetscCall(DMPlexCreateRankField(dm, &ranks));
1770     PetscCall(VecView(ranks, viewer));
1771     PetscCall(VecDestroy(&ranks));
1772   }
1773   /* Optionally view a label */
1774   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1775   if (flg) {
1776     DMLabel label;
1777     Vec     val;
1778 
1779     PetscCall(DMGetLabel(dm, name, &label));
1780     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1781     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1782     PetscCall(VecView(val, viewer));
1783     PetscCall(VecDestroy(&val));
1784   }
1785   PetscFunctionReturn(0);
1786 }
1787 
1788 /*@
1789   DMPlexTopologyView - Saves a DMPlex topology into a file
1790 
1791   Collective on DM
1792 
1793   Input Parameters:
1794 + dm                - The DM whose topology is to be saved
1795 - viewer            - The PetscViewer for saving
1796 
1797   Level: advanced
1798 
1799 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1800 @*/
1801 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer) {
1802   PetscBool ishdf5;
1803 
1804   PetscFunctionBegin;
1805   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1806   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1807   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1808   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1809   if (ishdf5) {
1810 #if defined(PETSC_HAVE_HDF5)
1811     PetscViewerFormat format;
1812     PetscCall(PetscViewerGetFormat(viewer, &format));
1813     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1814       IS globalPointNumbering;
1815 
1816       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1817       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1818       PetscCall(ISDestroy(&globalPointNumbering));
1819     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1820 #else
1821     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1822 #endif
1823   }
1824   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1825   PetscFunctionReturn(0);
1826 }
1827 
1828 /*@
1829   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1830 
1831   Collective on DM
1832 
1833   Input Parameters:
1834 + dm     - The DM whose coordinates are to be saved
1835 - viewer - The PetscViewer for saving
1836 
1837   Level: advanced
1838 
1839 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1840 @*/
1841 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer) {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1855     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1856 #else
1857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1858 #endif
1859   }
1860   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1861   PetscFunctionReturn(0);
1862 }
1863 
1864 /*@
1865   DMPlexLabelsView - Saves DMPlex labels into a file
1866 
1867   Collective on DM
1868 
1869   Input Parameters:
1870 + dm     - The DM whose labels are to be saved
1871 - viewer - The PetscViewer for saving
1872 
1873   Level: advanced
1874 
1875 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1876 @*/
1877 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer) {
1878   PetscBool ishdf5;
1879 
1880   PetscFunctionBegin;
1881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1882   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1883   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1884   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1885   if (ishdf5) {
1886 #if defined(PETSC_HAVE_HDF5)
1887     IS                globalPointNumbering;
1888     PetscViewerFormat format;
1889 
1890     PetscCall(PetscViewerGetFormat(viewer, &format));
1891     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1892       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1893       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1894       PetscCall(ISDestroy(&globalPointNumbering));
1895     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1896 #else
1897     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1898 #endif
1899   }
1900   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1901   PetscFunctionReturn(0);
1902 }
1903 
1904 /*@
1905   DMPlexSectionView - Saves a section associated with a DMPlex
1906 
1907   Collective on DM
1908 
1909   Input Parameters:
1910 + dm         - The DM that contains the topology on which the section to be saved is defined
1911 . viewer     - The PetscViewer for saving
1912 - sectiondm  - The DM that contains the section to be saved
1913 
1914   Level: advanced
1915 
1916   Notes:
1917   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.
1918 
1919   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.
1920 
1921 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1922 @*/
1923 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm) {
1924   PetscBool ishdf5;
1925 
1926   PetscFunctionBegin;
1927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1928   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1929   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1931   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1932   if (ishdf5) {
1933 #if defined(PETSC_HAVE_HDF5)
1934     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1935 #else
1936     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1937 #endif
1938   }
1939   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1940   PetscFunctionReturn(0);
1941 }
1942 
1943 /*@
1944   DMPlexGlobalVectorView - Saves a global vector
1945 
1946   Collective on DM
1947 
1948   Input Parameters:
1949 + dm        - The DM that represents the topology
1950 . viewer    - The PetscViewer to save data with
1951 . sectiondm - The DM that contains the global section on which vec is defined
1952 - vec       - The global vector to be saved
1953 
1954   Level: advanced
1955 
1956   Notes:
1957   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.
1958 
1959   Typical calling sequence
1960 $       DMCreate(PETSC_COMM_WORLD, &dm);
1961 $       DMSetType(dm, DMPLEX);
1962 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1963 $       DMClone(dm, &sectiondm);
1964 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1965 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1966 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1967 $       PetscSectionSetChart(section, pStart, pEnd);
1968 $       PetscSectionSetUp(section);
1969 $       DMSetLocalSection(sectiondm, section);
1970 $       PetscSectionDestroy(&section);
1971 $       DMGetGlobalVector(sectiondm, &vec);
1972 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1973 $       DMPlexTopologyView(dm, viewer);
1974 $       DMPlexSectionView(dm, viewer, sectiondm);
1975 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1976 $       DMRestoreGlobalVector(sectiondm, &vec);
1977 $       DMDestroy(&sectiondm);
1978 $       DMDestroy(&dm);
1979 
1980 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1981 @*/
1982 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
1983   PetscBool ishdf5;
1984 
1985   PetscFunctionBegin;
1986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1987   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1988   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1989   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1990   /* Check consistency */
1991   {
1992     PetscSection section;
1993     PetscBool    includesConstraints;
1994     PetscInt     m, m1;
1995 
1996     PetscCall(VecGetLocalSize(vec, &m1));
1997     PetscCall(DMGetGlobalSection(sectiondm, &section));
1998     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1999     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2000     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2001     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2002   }
2003   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2004   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2005   if (ishdf5) {
2006 #if defined(PETSC_HAVE_HDF5)
2007     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2008 #else
2009     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2010 #endif
2011   }
2012   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2013   PetscFunctionReturn(0);
2014 }
2015 
2016 /*@
2017   DMPlexLocalVectorView - Saves a local vector
2018 
2019   Collective on DM
2020 
2021   Input Parameters:
2022 + dm        - The DM that represents the topology
2023 . viewer    - The PetscViewer to save data with
2024 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2025 - vec       - The local vector to be saved
2026 
2027   Level: advanced
2028 
2029   Notes:
2030   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.
2031 
2032   Typical calling sequence
2033 $       DMCreate(PETSC_COMM_WORLD, &dm);
2034 $       DMSetType(dm, DMPLEX);
2035 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2036 $       DMClone(dm, &sectiondm);
2037 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2038 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2039 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2040 $       PetscSectionSetChart(section, pStart, pEnd);
2041 $       PetscSectionSetUp(section);
2042 $       DMSetLocalSection(sectiondm, section);
2043 $       DMGetLocalVector(sectiondm, &vec);
2044 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2045 $       DMPlexTopologyView(dm, viewer);
2046 $       DMPlexSectionView(dm, viewer, sectiondm);
2047 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2048 $       DMRestoreLocalVector(sectiondm, &vec);
2049 $       DMDestroy(&sectiondm);
2050 $       DMDestroy(&dm);
2051 
2052 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2053 @*/
2054 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
2055   PetscBool ishdf5;
2056 
2057   PetscFunctionBegin;
2058   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2059   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2060   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2061   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2062   /* Check consistency */
2063   {
2064     PetscSection section;
2065     PetscBool    includesConstraints;
2066     PetscInt     m, m1;
2067 
2068     PetscCall(VecGetLocalSize(vec, &m1));
2069     PetscCall(DMGetLocalSection(sectiondm, &section));
2070     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2071     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2072     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2073     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2074   }
2075   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2076   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2077   if (ishdf5) {
2078 #if defined(PETSC_HAVE_HDF5)
2079     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2080 #else
2081     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2082 #endif
2083   }
2084   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2085   PetscFunctionReturn(0);
2086 }
2087 
2088 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer) {
2089   PetscBool ishdf5;
2090 
2091   PetscFunctionBegin;
2092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2093   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2094   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2095   if (ishdf5) {
2096 #if defined(PETSC_HAVE_HDF5)
2097     PetscViewerFormat format;
2098     PetscCall(PetscViewerGetFormat(viewer, &format));
2099     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2100       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2101     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2102       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2103     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2104     PetscFunctionReturn(0);
2105 #else
2106     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2107 #endif
2108   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2109 }
2110 
2111 /*@
2112   DMPlexTopologyLoad - Loads a topology into a DMPlex
2113 
2114   Collective on DM
2115 
2116   Input Parameters:
2117 + dm                - The DM into which the topology is loaded
2118 - viewer            - The PetscViewer for the saved topology
2119 
2120   Output Parameters:
2121 . 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
2122 
2123   Level: advanced
2124 
2125 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2126 @*/
2127 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF) {
2128   PetscBool ishdf5;
2129 
2130   PetscFunctionBegin;
2131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2132   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2133   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2134   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2135   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2136   if (ishdf5) {
2137 #if defined(PETSC_HAVE_HDF5)
2138     PetscViewerFormat format;
2139     PetscCall(PetscViewerGetFormat(viewer, &format));
2140     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2141       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2142     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2143 #else
2144     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2145 #endif
2146   }
2147   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2148   PetscFunctionReturn(0);
2149 }
2150 
2151 /*@
2152   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2153 
2154   Collective on DM
2155 
2156   Input Parameters:
2157 + dm     - The DM into which the coordinates are loaded
2158 . viewer - The PetscViewer for the saved coordinates
2159 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2160 
2161   Level: advanced
2162 
2163 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2164 @*/
2165 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2166   PetscBool ishdf5;
2167 
2168   PetscFunctionBegin;
2169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2170   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2171   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2172   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2173   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2174   if (ishdf5) {
2175 #if defined(PETSC_HAVE_HDF5)
2176     PetscViewerFormat format;
2177     PetscCall(PetscViewerGetFormat(viewer, &format));
2178     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2179       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2180     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2181 #else
2182     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2183 #endif
2184   }
2185   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2186   PetscFunctionReturn(0);
2187 }
2188 
2189 /*@
2190   DMPlexLabelsLoad - Loads labels into a DMPlex
2191 
2192   Collective on DM
2193 
2194   Input Parameters:
2195 + dm     - The DM into which the labels are loaded
2196 . viewer - The PetscViewer for the saved labels
2197 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2198 
2199   Level: advanced
2200 
2201   Notes:
2202   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2203 
2204 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2205 @*/
2206 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2207   PetscBool ishdf5;
2208 
2209   PetscFunctionBegin;
2210   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2211   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2212   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2213   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2214   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2215   if (ishdf5) {
2216 #if defined(PETSC_HAVE_HDF5)
2217     PetscViewerFormat format;
2218 
2219     PetscCall(PetscViewerGetFormat(viewer, &format));
2220     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2221       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2222     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2223 #else
2224     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2225 #endif
2226   }
2227   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2228   PetscFunctionReturn(0);
2229 }
2230 
2231 /*@
2232   DMPlexSectionLoad - Loads section into a DMPlex
2233 
2234   Collective on DM
2235 
2236   Input Parameters:
2237 + dm          - The DM that represents the topology
2238 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2239 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2240 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2241 
2242   Output Parameters
2243 + 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)
2244 - 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)
2245 
2246   Level: advanced
2247 
2248   Notes:
2249   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.
2250 
2251   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.
2252 
2253   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.
2254 
2255   Example using 2 processes:
2256 $  NX (number of points on dm): 4
2257 $  sectionA                   : the on-disk section
2258 $  vecA                       : a vector associated with sectionA
2259 $  sectionB                   : sectiondm's local section constructed in this function
2260 $  vecB (local)               : a vector associated with sectiondm's local section
2261 $  vecB (global)              : a vector associated with sectiondm's global section
2262 $
2263 $                                     rank 0    rank 1
2264 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2265 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2266 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2267 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2268 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2269 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2270 $  sectionB->atlasDof             :     1 0 1 | 1 3
2271 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2272 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2273 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2274 $
2275 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2276 
2277 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2278 @*/
2279 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF) {
2280   PetscBool ishdf5;
2281 
2282   PetscFunctionBegin;
2283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2284   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2285   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2286   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2287   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2288   if (localDofSF) PetscValidPointer(localDofSF, 6);
2289   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2290   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2291   if (ishdf5) {
2292 #if defined(PETSC_HAVE_HDF5)
2293     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2294 #else
2295     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2296 #endif
2297   }
2298   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2299   PetscFunctionReturn(0);
2300 }
2301 
2302 /*@
2303   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2304 
2305   Collective on DM
2306 
2307   Input Parameters:
2308 + dm        - The DM that represents the topology
2309 . viewer    - The PetscViewer that represents the on-disk vector data
2310 . sectiondm - The DM that contains the global section on which vec is defined
2311 . sf        - The SF that migrates the on-disk vector data into vec
2312 - vec       - The global vector to set values of
2313 
2314   Level: advanced
2315 
2316   Notes:
2317   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.
2318 
2319   Typical calling sequence
2320 $       DMCreate(PETSC_COMM_WORLD, &dm);
2321 $       DMSetType(dm, DMPLEX);
2322 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2323 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2324 $       DMClone(dm, &sectiondm);
2325 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2326 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2327 $       DMGetGlobalVector(sectiondm, &vec);
2328 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2329 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2330 $       DMRestoreGlobalVector(sectiondm, &vec);
2331 $       PetscSFDestroy(&gsf);
2332 $       PetscSFDestroy(&sfX);
2333 $       DMDestroy(&sectiondm);
2334 $       DMDestroy(&dm);
2335 
2336 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2337 @*/
2338 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2339   PetscBool ishdf5;
2340 
2341   PetscFunctionBegin;
2342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2344   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2345   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2346   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2347   /* Check consistency */
2348   {
2349     PetscSection section;
2350     PetscBool    includesConstraints;
2351     PetscInt     m, m1;
2352 
2353     PetscCall(VecGetLocalSize(vec, &m1));
2354     PetscCall(DMGetGlobalSection(sectiondm, &section));
2355     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2356     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2357     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2358     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2359   }
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2370   PetscFunctionReturn(0);
2371 }
2372 
2373 /*@
2374   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2375 
2376   Collective on DM
2377 
2378   Input Parameters:
2379 + dm        - The DM that represents the topology
2380 . viewer    - The PetscViewer that represents the on-disk vector data
2381 . sectiondm - The DM that contains the local section on which vec is defined
2382 . sf        - The SF that migrates the on-disk vector data into vec
2383 - vec       - The local vector to set values of
2384 
2385   Level: advanced
2386 
2387   Notes:
2388   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2389 
2390   Typical calling sequence
2391 $       DMCreate(PETSC_COMM_WORLD, &dm);
2392 $       DMSetType(dm, DMPLEX);
2393 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2394 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2395 $       DMClone(dm, &sectiondm);
2396 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2397 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2398 $       DMGetLocalVector(sectiondm, &vec);
2399 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2400 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2401 $       DMRestoreLocalVector(sectiondm, &vec);
2402 $       PetscSFDestroy(&lsf);
2403 $       PetscSFDestroy(&sfX);
2404 $       DMDestroy(&sectiondm);
2405 $       DMDestroy(&dm);
2406 
2407 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2408 @*/
2409 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2410   PetscBool ishdf5;
2411 
2412   PetscFunctionBegin;
2413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2414   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2415   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2416   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2417   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2418   /* Check consistency */
2419   {
2420     PetscSection section;
2421     PetscBool    includesConstraints;
2422     PetscInt     m, m1;
2423 
2424     PetscCall(VecGetLocalSize(vec, &m1));
2425     PetscCall(DMGetLocalSection(sectiondm, &section));
2426     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2427     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2428     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2429     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2430   }
2431   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2432   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2433   if (ishdf5) {
2434 #if defined(PETSC_HAVE_HDF5)
2435     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2436 #else
2437     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2438 #endif
2439   }
2440   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2441   PetscFunctionReturn(0);
2442 }
2443 
2444 PetscErrorCode DMDestroy_Plex(DM dm) {
2445   DM_Plex *mesh = (DM_Plex *)dm->data;
2446 
2447   PetscFunctionBegin;
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2461   if (--mesh->refct > 0) PetscFunctionReturn(0);
2462   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2463   PetscCall(PetscFree(mesh->cones));
2464   PetscCall(PetscFree(mesh->coneOrientations));
2465   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2466   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2467   PetscCall(PetscFree(mesh->supports));
2468   PetscCall(PetscFree(mesh->facesTmp));
2469   PetscCall(PetscFree(mesh->tetgenOpts));
2470   PetscCall(PetscFree(mesh->triangleOpts));
2471   PetscCall(PetscFree(mesh->transformType));
2472   PetscCall(PetscFree(mesh->distributionName));
2473   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2474   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2475   PetscCall(ISDestroy(&mesh->subpointIS));
2476   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2477   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2478   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2479   PetscCall(ISDestroy(&mesh->anchorIS));
2480   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2481   PetscCall(PetscFree(mesh->parents));
2482   PetscCall(PetscFree(mesh->childIDs));
2483   PetscCall(PetscSectionDestroy(&mesh->childSection));
2484   PetscCall(PetscFree(mesh->children));
2485   PetscCall(DMDestroy(&mesh->referenceTree));
2486   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2487   PetscCall(PetscFree(mesh->neighbors));
2488   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2489   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2490   PetscCall(PetscFree(mesh));
2491   PetscFunctionReturn(0);
2492 }
2493 
2494 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J) {
2495   PetscSection           sectionGlobal;
2496   PetscInt               bs = -1, mbs;
2497   PetscInt               localSize, localStart = 0;
2498   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2499   MatType                mtype;
2500   ISLocalToGlobalMapping ltog;
2501 
2502   PetscFunctionBegin;
2503   PetscCall(MatInitializePackage());
2504   mtype = dm->mattype;
2505   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2506   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2507   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2508   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2509   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2510   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2511   PetscCall(MatSetType(*J, mtype));
2512   PetscCall(MatSetFromOptions(*J));
2513   PetscCall(MatGetBlockSize(*J, &mbs));
2514   if (mbs > 1) bs = mbs;
2515   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2516   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2517   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2518   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2519   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2520   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2521   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2522   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2523   if (!isShell) {
2524     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2525     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2526     PetscInt  pStart, pEnd, p, dof, cdof;
2527 
2528     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2529 
2530     PetscCall(PetscCalloc1(localSize, &pblocks));
2531     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2532     for (p = pStart; p < pEnd; ++p) {
2533       PetscInt bdof, offset;
2534 
2535       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2536       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2537       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2538       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2539       dof  = dof < 0 ? -(dof + 1) : dof;
2540       bdof = cdof && (dof - cdof) ? 1 : dof;
2541       if (dof) {
2542         if (bs < 0) {
2543           bs = bdof;
2544         } else if (bs != bdof) {
2545           bs = 1;
2546         }
2547       }
2548     }
2549     /* Must have same blocksize on all procs (some might have no points) */
2550     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2551     bsLocal[1] = bs;
2552     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2553     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2554     else bs = bsMinMax[0];
2555     bs = PetscMax(1, bs);
2556     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2557     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2558       PetscCall(MatSetBlockSize(*J, bs));
2559       PetscCall(MatSetUp(*J));
2560     } else {
2561       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2562       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2563       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2564     }
2565     { // Consolidate blocks
2566       PetscInt nblocks = 0;
2567       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2568         if (pblocks[i] == 0) continue;
2569         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2570         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]);
2571       }
2572       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2573     }
2574     PetscCall(PetscFree(pblocks));
2575   }
2576   PetscCall(MatSetDM(*J, dm));
2577   PetscFunctionReturn(0);
2578 }
2579 
2580 /*@
2581   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2582 
2583   Not collective
2584 
2585   Input Parameter:
2586 . mesh - The DMPlex
2587 
2588   Output Parameters:
2589 . subsection - The subdomain section
2590 
2591   Level: developer
2592 
2593 .seealso:
2594 @*/
2595 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection) {
2596   DM_Plex *mesh = (DM_Plex *)dm->data;
2597 
2598   PetscFunctionBegin;
2599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2600   if (!mesh->subdomainSection) {
2601     PetscSection section;
2602     PetscSF      sf;
2603 
2604     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2605     PetscCall(DMGetLocalSection(dm, &section));
2606     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2607     PetscCall(PetscSFDestroy(&sf));
2608   }
2609   *subsection = mesh->subdomainSection;
2610   PetscFunctionReturn(0);
2611 }
2612 
2613 /*@
2614   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2615 
2616   Not collective
2617 
2618   Input Parameter:
2619 . mesh - The DMPlex
2620 
2621   Output Parameters:
2622 + pStart - The first mesh point
2623 - pEnd   - The upper bound for mesh points
2624 
2625   Level: beginner
2626 
2627 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2628 @*/
2629 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd) {
2630   DM_Plex *mesh = (DM_Plex *)dm->data;
2631 
2632   PetscFunctionBegin;
2633   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2634   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2635   PetscFunctionReturn(0);
2636 }
2637 
2638 /*@
2639   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2640 
2641   Not collective
2642 
2643   Input Parameters:
2644 + mesh - The DMPlex
2645 . pStart - The first mesh point
2646 - pEnd   - The upper bound for mesh points
2647 
2648   Output Parameters:
2649 
2650   Level: beginner
2651 
2652 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2653 @*/
2654 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd) {
2655   DM_Plex *mesh = (DM_Plex *)dm->data;
2656 
2657   PetscFunctionBegin;
2658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2659   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2660   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2661   PetscFunctionReturn(0);
2662 }
2663 
2664 /*@
2665   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2666 
2667   Not collective
2668 
2669   Input Parameters:
2670 + mesh - The DMPlex
2671 - p - The point, which must lie in the chart set with DMPlexSetChart()
2672 
2673   Output Parameter:
2674 . size - The cone size for point p
2675 
2676   Level: beginner
2677 
2678 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2679 @*/
2680 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size) {
2681   DM_Plex *mesh = (DM_Plex *)dm->data;
2682 
2683   PetscFunctionBegin;
2684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2685   PetscValidIntPointer(size, 3);
2686   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2687   PetscFunctionReturn(0);
2688 }
2689 
2690 /*@
2691   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2692 
2693   Not collective
2694 
2695   Input Parameters:
2696 + mesh - The DMPlex
2697 . p - The point, which must lie in the chart set with DMPlexSetChart()
2698 - size - The cone size for point p
2699 
2700   Output Parameter:
2701 
2702   Note:
2703   This should be called after DMPlexSetChart().
2704 
2705   Level: beginner
2706 
2707 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2708 @*/
2709 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size) {
2710   DM_Plex *mesh = (DM_Plex *)dm->data;
2711 
2712   PetscFunctionBegin;
2713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2714   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2715   PetscFunctionReturn(0);
2716 }
2717 
2718 /*@
2719   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2720 
2721   Not collective
2722 
2723   Input Parameters:
2724 + mesh - The DMPlex
2725 . p - The point, which must lie in the chart set with DMPlexSetChart()
2726 - size - The additional cone size for point p
2727 
2728   Output Parameter:
2729 
2730   Note:
2731   This should be called after DMPlexSetChart().
2732 
2733   Level: beginner
2734 
2735 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2736 @*/
2737 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size) {
2738   DM_Plex *mesh = (DM_Plex *)dm->data;
2739   PetscFunctionBegin;
2740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2741   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2742   PetscFunctionReturn(0);
2743 }
2744 
2745 /*@C
2746   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2747 
2748   Not collective
2749 
2750   Input Parameters:
2751 + dm - The DMPlex
2752 - p - The point, which must lie in the chart set with DMPlexSetChart()
2753 
2754   Output Parameter:
2755 . cone - An array of points which are on the in-edges for point p
2756 
2757   Level: beginner
2758 
2759   Fortran Notes:
2760   Since it returns an array, this routine is only available in Fortran 90, and you must
2761   include petsc.h90 in your code.
2762   You must also call DMPlexRestoreCone() after you finish using the returned array.
2763   DMPlexRestoreCone() is not needed/available in C.
2764 
2765 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2766 @*/
2767 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[]) {
2768   DM_Plex *mesh = (DM_Plex *)dm->data;
2769   PetscInt off;
2770 
2771   PetscFunctionBegin;
2772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2773   PetscValidPointer(cone, 3);
2774   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2775   *cone = &mesh->cones[off];
2776   PetscFunctionReturn(0);
2777 }
2778 
2779 /*@C
2780   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2781 
2782   Not collective
2783 
2784   Input Parameters:
2785 + dm - The DMPlex
2786 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2787 
2788   Output Parameters:
2789 + pConesSection - PetscSection describing the layout of pCones
2790 - pCones - An array of points which are on the in-edges for the point set p
2791 
2792   Level: intermediate
2793 
2794 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2795 @*/
2796 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones) {
2797   PetscSection cs, newcs;
2798   PetscInt    *cones;
2799   PetscInt    *newarr = NULL;
2800   PetscInt     n;
2801 
2802   PetscFunctionBegin;
2803   PetscCall(DMPlexGetCones(dm, &cones));
2804   PetscCall(DMPlexGetConeSection(dm, &cs));
2805   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2806   if (pConesSection) *pConesSection = newcs;
2807   if (pCones) {
2808     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2809     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2810   }
2811   PetscFunctionReturn(0);
2812 }
2813 
2814 /*@
2815   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2816 
2817   Not collective
2818 
2819   Input Parameters:
2820 + dm - The DMPlex
2821 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2822 
2823   Output Parameter:
2824 . expandedPoints - An array of vertices recursively expanded from input points
2825 
2826   Level: advanced
2827 
2828   Notes:
2829   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2830   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2831 
2832 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2833 @*/
2834 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints) {
2835   IS      *expandedPointsAll;
2836   PetscInt depth;
2837 
2838   PetscFunctionBegin;
2839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2840   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2841   PetscValidPointer(expandedPoints, 3);
2842   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2843   *expandedPoints = expandedPointsAll[0];
2844   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2845   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2846   PetscFunctionReturn(0);
2847 }
2848 
2849 /*@
2850   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).
2851 
2852   Not collective
2853 
2854   Input Parameters:
2855 + dm - The DMPlex
2856 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2857 
2858   Output Parameters:
2859 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2860 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2861 - sections - (optional) An array of sections which describe mappings from points to their cone points
2862 
2863   Level: advanced
2864 
2865   Notes:
2866   Like DMPlexGetConeTuple() but recursive.
2867 
2868   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.
2869   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2870 
2871   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:
2872   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2873   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2874 
2875 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2876 @*/
2877 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2878   const PetscInt *arr0 = NULL, *cone = NULL;
2879   PetscInt       *arr = NULL, *newarr = NULL;
2880   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2881   IS             *expandedPoints_;
2882   PetscSection   *sections_;
2883 
2884   PetscFunctionBegin;
2885   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2886   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2887   if (depth) PetscValidIntPointer(depth, 3);
2888   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2889   if (sections) PetscValidPointer(sections, 5);
2890   PetscCall(ISGetLocalSize(points, &n));
2891   PetscCall(ISGetIndices(points, &arr0));
2892   PetscCall(DMPlexGetDepth(dm, &depth_));
2893   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2894   PetscCall(PetscCalloc1(depth_, &sections_));
2895   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2896   for (d = depth_ - 1; d >= 0; d--) {
2897     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2898     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2899     for (i = 0; i < n; i++) {
2900       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2901       if (arr[i] >= start && arr[i] < end) {
2902         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2903         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2904       } else {
2905         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2906       }
2907     }
2908     PetscCall(PetscSectionSetUp(sections_[d]));
2909     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2910     PetscCall(PetscMalloc1(newn, &newarr));
2911     for (i = 0; i < n; i++) {
2912       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2913       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2914       if (cn > 1) {
2915         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2916         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2917       } else {
2918         newarr[co] = arr[i];
2919       }
2920     }
2921     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2922     arr = newarr;
2923     n   = newn;
2924   }
2925   PetscCall(ISRestoreIndices(points, &arr0));
2926   *depth = depth_;
2927   if (expandedPoints) *expandedPoints = expandedPoints_;
2928   else {
2929     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2930     PetscCall(PetscFree(expandedPoints_));
2931   }
2932   if (sections) *sections = sections_;
2933   else {
2934     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2935     PetscCall(PetscFree(sections_));
2936   }
2937   PetscFunctionReturn(0);
2938 }
2939 
2940 /*@
2941   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2942 
2943   Not collective
2944 
2945   Input Parameters:
2946 + dm - The DMPlex
2947 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2948 
2949   Output Parameters:
2950 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2951 . expandedPoints - (optional) An array of recursively expanded cones
2952 - sections - (optional) An array of sections which describe mappings from points to their cone points
2953 
2954   Level: advanced
2955 
2956   Notes:
2957   See DMPlexGetConeRecursive() for details.
2958 
2959 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2960 @*/
2961 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2962   PetscInt d, depth_;
2963 
2964   PetscFunctionBegin;
2965   PetscCall(DMPlexGetDepth(dm, &depth_));
2966   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2967   if (depth) *depth = 0;
2968   if (expandedPoints) {
2969     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2970     PetscCall(PetscFree(*expandedPoints));
2971   }
2972   if (sections) {
2973     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2974     PetscCall(PetscFree(*sections));
2975   }
2976   PetscFunctionReturn(0);
2977 }
2978 
2979 /*@
2980   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
2981 
2982   Not collective
2983 
2984   Input Parameters:
2985 + mesh - The DMPlex
2986 . p - The point, which must lie in the chart set with DMPlexSetChart()
2987 - cone - An array of points which are on the in-edges for point p
2988 
2989   Output Parameter:
2990 
2991   Note:
2992   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2993 
2994   Level: beginner
2995 
2996 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
2997 @*/
2998 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[]) {
2999   DM_Plex *mesh = (DM_Plex *)dm->data;
3000   PetscInt pStart, pEnd;
3001   PetscInt dof, off, c;
3002 
3003   PetscFunctionBegin;
3004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3005   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3006   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3007   if (dof) PetscValidIntPointer(cone, 3);
3008   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3009   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);
3010   for (c = 0; c < dof; ++c) {
3011     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);
3012     mesh->cones[off + c] = cone[c];
3013   }
3014   PetscFunctionReturn(0);
3015 }
3016 
3017 /*@C
3018   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3019 
3020   Not collective
3021 
3022   Input Parameters:
3023 + mesh - The DMPlex
3024 - p - The point, which must lie in the chart set with DMPlexSetChart()
3025 
3026   Output Parameter:
3027 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3028                     integer giving the prescription for cone traversal.
3029 
3030   Level: beginner
3031 
3032   Notes:
3033   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3034   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3035   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3036   with the identity.
3037 
3038   Fortran Notes:
3039   Since it returns an array, this routine is only available in Fortran 90, and you must
3040   include petsc.h90 in your code.
3041   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3042   DMPlexRestoreConeOrientation() is not needed/available in C.
3043 
3044 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3045 @*/
3046 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[]) {
3047   DM_Plex *mesh = (DM_Plex *)dm->data;
3048   PetscInt off;
3049 
3050   PetscFunctionBegin;
3051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3052   if (PetscDefined(USE_DEBUG)) {
3053     PetscInt dof;
3054     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3055     if (dof) PetscValidPointer(coneOrientation, 3);
3056   }
3057   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3058 
3059   *coneOrientation = &mesh->coneOrientations[off];
3060   PetscFunctionReturn(0);
3061 }
3062 
3063 /*@
3064   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3065 
3066   Not collective
3067 
3068   Input Parameters:
3069 + mesh - The DMPlex
3070 . p - The point, which must lie in the chart set with DMPlexSetChart()
3071 - coneOrientation - An array of orientations
3072   Output Parameter:
3073 
3074   Notes:
3075   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3076 
3077   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3078 
3079   Level: beginner
3080 
3081 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3082 @*/
3083 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[]) {
3084   DM_Plex *mesh = (DM_Plex *)dm->data;
3085   PetscInt pStart, pEnd;
3086   PetscInt dof, off, c;
3087 
3088   PetscFunctionBegin;
3089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3090   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3091   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3092   if (dof) PetscValidIntPointer(coneOrientation, 3);
3093   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3094   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);
3095   for (c = 0; c < dof; ++c) {
3096     PetscInt cdof, o = coneOrientation[c];
3097 
3098     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3099     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);
3100     mesh->coneOrientations[off + c] = o;
3101   }
3102   PetscFunctionReturn(0);
3103 }
3104 
3105 /*@
3106   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3107 
3108   Not collective
3109 
3110   Input Parameters:
3111 + mesh - The DMPlex
3112 . p - The point, which must lie in the chart set with DMPlexSetChart()
3113 . conePos - The local index in the cone where the point should be put
3114 - conePoint - The mesh point to insert
3115 
3116   Level: beginner
3117 
3118 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3119 @*/
3120 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint) {
3121   DM_Plex *mesh = (DM_Plex *)dm->data;
3122   PetscInt pStart, pEnd;
3123   PetscInt dof, off;
3124 
3125   PetscFunctionBegin;
3126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3127   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3128   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);
3129   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);
3130   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3131   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3132   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);
3133   mesh->cones[off + conePos] = conePoint;
3134   PetscFunctionReturn(0);
3135 }
3136 
3137 /*@
3138   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3139 
3140   Not collective
3141 
3142   Input Parameters:
3143 + mesh - The DMPlex
3144 . p - The point, which must lie in the chart set with DMPlexSetChart()
3145 . conePos - The local index in the cone where the point should be put
3146 - coneOrientation - The point orientation to insert
3147 
3148   Level: beginner
3149 
3150   Notes:
3151   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3152 
3153 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3154 @*/
3155 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation) {
3156   DM_Plex *mesh = (DM_Plex *)dm->data;
3157   PetscInt pStart, pEnd;
3158   PetscInt dof, off;
3159 
3160   PetscFunctionBegin;
3161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3162   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3163   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3164   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3165   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3166   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);
3167   mesh->coneOrientations[off + conePos] = coneOrientation;
3168   PetscFunctionReturn(0);
3169 }
3170 
3171 /*@
3172   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3173 
3174   Not collective
3175 
3176   Input Parameters:
3177 + mesh - The DMPlex
3178 - p - The point, which must lie in the chart set with DMPlexSetChart()
3179 
3180   Output Parameter:
3181 . size - The support size for point p
3182 
3183   Level: beginner
3184 
3185 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3186 @*/
3187 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size) {
3188   DM_Plex *mesh = (DM_Plex *)dm->data;
3189 
3190   PetscFunctionBegin;
3191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3192   PetscValidIntPointer(size, 3);
3193   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3194   PetscFunctionReturn(0);
3195 }
3196 
3197 /*@
3198   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3199 
3200   Not collective
3201 
3202   Input Parameters:
3203 + mesh - The DMPlex
3204 . p - The point, which must lie in the chart set with DMPlexSetChart()
3205 - size - The support size for point p
3206 
3207   Output Parameter:
3208 
3209   Note:
3210   This should be called after DMPlexSetChart().
3211 
3212   Level: beginner
3213 
3214 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3215 @*/
3216 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size) {
3217   DM_Plex *mesh = (DM_Plex *)dm->data;
3218 
3219   PetscFunctionBegin;
3220   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3221   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3222   PetscFunctionReturn(0);
3223 }
3224 
3225 /*@C
3226   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3227 
3228   Not collective
3229 
3230   Input Parameters:
3231 + mesh - The DMPlex
3232 - p - The point, which must lie in the chart set with DMPlexSetChart()
3233 
3234   Output Parameter:
3235 . support - An array of points which are on the out-edges for point p
3236 
3237   Level: beginner
3238 
3239   Fortran Notes:
3240   Since it returns an array, this routine is only available in Fortran 90, and you must
3241   include petsc.h90 in your code.
3242   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3243   DMPlexRestoreSupport() is not needed/available in C.
3244 
3245 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3246 @*/
3247 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[]) {
3248   DM_Plex *mesh = (DM_Plex *)dm->data;
3249   PetscInt off;
3250 
3251   PetscFunctionBegin;
3252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3253   PetscValidPointer(support, 3);
3254   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3255   *support = &mesh->supports[off];
3256   PetscFunctionReturn(0);
3257 }
3258 
3259 /*@
3260   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3261 
3262   Not collective
3263 
3264   Input Parameters:
3265 + mesh - The DMPlex
3266 . p - The point, which must lie in the chart set with DMPlexSetChart()
3267 - support - An array of points which are on the out-edges for point p
3268 
3269   Output Parameter:
3270 
3271   Note:
3272   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3273 
3274   Level: beginner
3275 
3276 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3277 @*/
3278 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[]) {
3279   DM_Plex *mesh = (DM_Plex *)dm->data;
3280   PetscInt pStart, pEnd;
3281   PetscInt dof, off, c;
3282 
3283   PetscFunctionBegin;
3284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3285   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3286   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3287   if (dof) PetscValidIntPointer(support, 3);
3288   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3289   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);
3290   for (c = 0; c < dof; ++c) {
3291     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);
3292     mesh->supports[off + c] = support[c];
3293   }
3294   PetscFunctionReturn(0);
3295 }
3296 
3297 /*@
3298   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3299 
3300   Not collective
3301 
3302   Input Parameters:
3303 + mesh - The DMPlex
3304 . p - The point, which must lie in the chart set with DMPlexSetChart()
3305 . supportPos - The local index in the cone where the point should be put
3306 - supportPoint - The mesh point to insert
3307 
3308   Level: beginner
3309 
3310 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3311 @*/
3312 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint) {
3313   DM_Plex *mesh = (DM_Plex *)dm->data;
3314   PetscInt pStart, pEnd;
3315   PetscInt dof, off;
3316 
3317   PetscFunctionBegin;
3318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3319   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3320   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3321   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3322   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);
3323   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);
3324   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);
3325   mesh->supports[off + supportPos] = supportPoint;
3326   PetscFunctionReturn(0);
3327 }
3328 
3329 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3330 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3331   switch (ct) {
3332   case DM_POLYTOPE_SEGMENT:
3333     if (o == -1) return -2;
3334     break;
3335   case DM_POLYTOPE_TRIANGLE:
3336     if (o == -3) return -1;
3337     if (o == -2) return -3;
3338     if (o == -1) return -2;
3339     break;
3340   case DM_POLYTOPE_QUADRILATERAL:
3341     if (o == -4) return -2;
3342     if (o == -3) return -1;
3343     if (o == -2) return -4;
3344     if (o == -1) return -3;
3345     break;
3346   default: return o;
3347   }
3348   return o;
3349 }
3350 
3351 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3352 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3353   switch (ct) {
3354   case DM_POLYTOPE_SEGMENT:
3355     if ((o == -2) || (o == 1)) return -1;
3356     if (o == -1) return 0;
3357     break;
3358   case DM_POLYTOPE_TRIANGLE:
3359     if (o == -3) return -2;
3360     if (o == -2) return -1;
3361     if (o == -1) return -3;
3362     break;
3363   case DM_POLYTOPE_QUADRILATERAL:
3364     if (o == -4) return -2;
3365     if (o == -3) return -1;
3366     if (o == -2) return -4;
3367     if (o == -1) return -3;
3368     break;
3369   default: return o;
3370   }
3371   return o;
3372 }
3373 
3374 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3375 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm) {
3376   PetscInt pStart, pEnd, p;
3377 
3378   PetscFunctionBegin;
3379   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3380   for (p = pStart; p < pEnd; ++p) {
3381     const PetscInt *cone, *ornt;
3382     PetscInt        coneSize, c;
3383 
3384     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3385     PetscCall(DMPlexGetCone(dm, p, &cone));
3386     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3387     for (c = 0; c < coneSize; ++c) {
3388       DMPolytopeType ct;
3389       const PetscInt o = ornt[c];
3390 
3391       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3392       switch (ct) {
3393       case DM_POLYTOPE_SEGMENT:
3394         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3395         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3396         break;
3397       case DM_POLYTOPE_TRIANGLE:
3398         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3399         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3400         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3401         break;
3402       case DM_POLYTOPE_QUADRILATERAL:
3403         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3404         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3405         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3406         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3407         break;
3408       default: break;
3409       }
3410     }
3411   }
3412   PetscFunctionReturn(0);
3413 }
3414 
3415 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3416   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3417   PetscInt       *closure;
3418   const PetscInt *tmp = NULL, *tmpO = NULL;
3419   PetscInt        off = 0, tmpSize, t;
3420 
3421   PetscFunctionBeginHot;
3422   if (ornt) {
3423     PetscCall(DMPlexGetCellType(dm, p, &ct));
3424     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3425   }
3426   if (*points) {
3427     closure = *points;
3428   } else {
3429     PetscInt maxConeSize, maxSupportSize;
3430     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3431     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3432   }
3433   if (useCone) {
3434     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3435     PetscCall(DMPlexGetCone(dm, p, &tmp));
3436     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3437   } else {
3438     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3439     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3440   }
3441   if (ct == DM_POLYTOPE_UNKNOWN) {
3442     closure[off++] = p;
3443     closure[off++] = 0;
3444     for (t = 0; t < tmpSize; ++t) {
3445       closure[off++] = tmp[t];
3446       closure[off++] = tmpO ? tmpO[t] : 0;
3447     }
3448   } else {
3449     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3450 
3451     /* We assume that cells with a valid type have faces with a valid type */
3452     closure[off++] = p;
3453     closure[off++] = ornt;
3454     for (t = 0; t < tmpSize; ++t) {
3455       DMPolytopeType ft;
3456 
3457       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3458       closure[off++] = tmp[arr[t]];
3459       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3460     }
3461   }
3462   if (numPoints) *numPoints = tmpSize + 1;
3463   if (points) *points = closure;
3464   PetscFunctionReturn(0);
3465 }
3466 
3467 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3468 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points) {
3469   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3470   const PetscInt *cone, *ornt;
3471   PetscInt       *pts, *closure = NULL;
3472   DMPolytopeType  ft;
3473   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3474   PetscInt        dim, coneSize, c, d, clSize, cl;
3475 
3476   PetscFunctionBeginHot;
3477   PetscCall(DMGetDimension(dm, &dim));
3478   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3479   PetscCall(DMPlexGetCone(dm, point, &cone));
3480   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3481   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3482   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3483   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3484   maxSize       = PetscMax(coneSeries, supportSeries);
3485   if (*points) {
3486     pts = *points;
3487   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3488   c        = 0;
3489   pts[c++] = point;
3490   pts[c++] = o;
3491   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3492   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3493   for (cl = 0; cl < clSize * 2; cl += 2) {
3494     pts[c++] = closure[cl];
3495     pts[c++] = closure[cl + 1];
3496   }
3497   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3498   for (cl = 0; cl < clSize * 2; cl += 2) {
3499     pts[c++] = closure[cl];
3500     pts[c++] = closure[cl + 1];
3501   }
3502   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3503   for (d = 2; d < coneSize; ++d) {
3504     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3505     pts[c++] = cone[arr[d * 2 + 0]];
3506     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3507   }
3508   if (dim >= 3) {
3509     for (d = 2; d < coneSize; ++d) {
3510       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3511       const PetscInt *fcone, *fornt;
3512       PetscInt        fconeSize, fc, i;
3513 
3514       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3515       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3516       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3517       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3518       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3519       for (fc = 0; fc < fconeSize; ++fc) {
3520         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3521         const PetscInt co = farr[fc * 2 + 1];
3522 
3523         for (i = 0; i < c; i += 2)
3524           if (pts[i] == cp) break;
3525         if (i == c) {
3526           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3527           pts[c++] = cp;
3528           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3529         }
3530       }
3531     }
3532   }
3533   *numPoints = c / 2;
3534   *points    = pts;
3535   PetscFunctionReturn(0);
3536 }
3537 
3538 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3539   DMPolytopeType ct;
3540   PetscInt      *closure, *fifo;
3541   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3542   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3543   PetscInt       depth, maxSize;
3544 
3545   PetscFunctionBeginHot;
3546   PetscCall(DMPlexGetDepth(dm, &depth));
3547   if (depth == 1) {
3548     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3549     PetscFunctionReturn(0);
3550   }
3551   PetscCall(DMPlexGetCellType(dm, p, &ct));
3552   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3553   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3554     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3555     PetscFunctionReturn(0);
3556   }
3557   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3558   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3559   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3560   maxSize       = PetscMax(coneSeries, supportSeries);
3561   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3562   if (*points) {
3563     closure = *points;
3564   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3565   closure[closureSize++] = p;
3566   closure[closureSize++] = ornt;
3567   fifo[fifoSize++]       = p;
3568   fifo[fifoSize++]       = ornt;
3569   fifo[fifoSize++]       = ct;
3570   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3571   while (fifoSize - fifoStart) {
3572     const PetscInt       q    = fifo[fifoStart++];
3573     const PetscInt       o    = fifo[fifoStart++];
3574     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3575     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3576     const PetscInt      *tmp, *tmpO;
3577     PetscInt             tmpSize, t;
3578 
3579     if (PetscDefined(USE_DEBUG)) {
3580       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3581       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);
3582     }
3583     if (useCone) {
3584       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3585       PetscCall(DMPlexGetCone(dm, q, &tmp));
3586       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3587     } else {
3588       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3589       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3590       tmpO = NULL;
3591     }
3592     for (t = 0; t < tmpSize; ++t) {
3593       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3594       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3595       const PetscInt cp = tmp[ip];
3596       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3597       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3598       PetscInt       c;
3599 
3600       /* Check for duplicate */
3601       for (c = 0; c < closureSize; c += 2) {
3602         if (closure[c] == cp) break;
3603       }
3604       if (c == closureSize) {
3605         closure[closureSize++] = cp;
3606         closure[closureSize++] = co;
3607         fifo[fifoSize++]       = cp;
3608         fifo[fifoSize++]       = co;
3609         fifo[fifoSize++]       = ct;
3610       }
3611     }
3612   }
3613   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3614   if (numPoints) *numPoints = closureSize / 2;
3615   if (points) *points = closure;
3616   PetscFunctionReturn(0);
3617 }
3618 
3619 /*@C
3620   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3621 
3622   Not collective
3623 
3624   Input Parameters:
3625 + dm      - The DMPlex
3626 . p       - The mesh point
3627 - useCone - PETSC_TRUE for the closure, otherwise return the star
3628 
3629   Input/Output Parameter:
3630 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3631            if NULL on input, internal storage will be returned, otherwise the provided array is used
3632 
3633   Output Parameter:
3634 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3635 
3636   Note:
3637   If using internal storage (points is NULL on input), each call overwrites the last output.
3638 
3639   Fortran Note:
3640   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3641 
3642   Level: beginner
3643 
3644 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3645 @*/
3646 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3647   PetscFunctionBeginHot;
3648   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3649   if (numPoints) PetscValidIntPointer(numPoints, 4);
3650   if (points) PetscValidPointer(points, 5);
3651   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3652   PetscFunctionReturn(0);
3653 }
3654 
3655 /*@C
3656   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3657 
3658   Not collective
3659 
3660   Input Parameters:
3661 + dm        - The DMPlex
3662 . p         - The mesh point
3663 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3664 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3665 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3666 
3667   Note:
3668   If not using internal storage (points is not NULL on input), this call is unnecessary
3669 
3670   Level: beginner
3671 
3672 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3673 @*/
3674 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3675   PetscFunctionBeginHot;
3676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3677   if (numPoints) *numPoints = 0;
3678   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3679   PetscFunctionReturn(0);
3680 }
3681 
3682 /*@
3683   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3684 
3685   Not collective
3686 
3687   Input Parameter:
3688 . mesh - The DMPlex
3689 
3690   Output Parameters:
3691 + maxConeSize - The maximum number of in-edges
3692 - maxSupportSize - The maximum number of out-edges
3693 
3694   Level: beginner
3695 
3696 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3697 @*/
3698 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize) {
3699   DM_Plex *mesh = (DM_Plex *)dm->data;
3700 
3701   PetscFunctionBegin;
3702   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3703   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3704   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3705   PetscFunctionReturn(0);
3706 }
3707 
3708 PetscErrorCode DMSetUp_Plex(DM dm) {
3709   DM_Plex *mesh = (DM_Plex *)dm->data;
3710   PetscInt size, maxSupportSize;
3711 
3712   PetscFunctionBegin;
3713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3714   PetscCall(PetscSectionSetUp(mesh->coneSection));
3715   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3716   PetscCall(PetscMalloc1(size, &mesh->cones));
3717   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3718   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3719   if (maxSupportSize) {
3720     PetscCall(PetscSectionSetUp(mesh->supportSection));
3721     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3722     PetscCall(PetscMalloc1(size, &mesh->supports));
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, PetscBool *contains) {
6432   PetscFunctionBegin;
6433   *contains = PETSC_TRUE;
6434   if (label) {
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(0);
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     PetscBool           contains;
6472 
6473     if (!fieldActive[f]) {
6474       for (p = 0; p < numPoints * 2; p += 2) {
6475         PetscInt fdof;
6476         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6477         offset += fdof;
6478       }
6479       continue;
6480     }
6481     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6482     switch (mode) {
6483     case INSERT_VALUES:
6484       for (p = 0; p < numPoints; p++) {
6485         const PetscInt     point = points[2 * p];
6486         const PetscInt    *perm  = perms ? perms[p] : NULL;
6487         const PetscScalar *flip  = flips ? flips[p] : NULL;
6488         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6489         if (!contains) continue;
6490         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6491       }
6492       break;
6493     case INSERT_ALL_VALUES:
6494       for (p = 0; p < numPoints; p++) {
6495         const PetscInt     point = points[2 * p];
6496         const PetscInt    *perm  = perms ? perms[p] : NULL;
6497         const PetscScalar *flip  = flips ? flips[p] : NULL;
6498         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6499         if (!contains) continue;
6500         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6501       }
6502       break;
6503     case INSERT_BC_VALUES:
6504       for (p = 0; p < numPoints; p++) {
6505         const PetscInt     point = points[2 * p];
6506         const PetscInt    *perm  = perms ? perms[p] : NULL;
6507         const PetscScalar *flip  = flips ? flips[p] : NULL;
6508         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6509         if (!contains) continue;
6510         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6511       }
6512       break;
6513     case ADD_VALUES:
6514       for (p = 0; p < numPoints; p++) {
6515         const PetscInt     point = points[2 * p];
6516         const PetscInt    *perm  = perms ? perms[p] : NULL;
6517         const PetscScalar *flip  = flips ? flips[p] : NULL;
6518         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6519         if (!contains) continue;
6520         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6521       }
6522       break;
6523     case ADD_ALL_VALUES:
6524       for (p = 0; p < numPoints; p++) {
6525         const PetscInt     point = points[2 * p];
6526         const PetscInt    *perm  = perms ? perms[p] : NULL;
6527         const PetscScalar *flip  = flips ? flips[p] : NULL;
6528         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6529         if (!contains) continue;
6530         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6531       }
6532       break;
6533     default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6534     }
6535     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6536   }
6537   /* Cleanup points */
6538   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6539   /* Cleanup array */
6540   PetscCall(VecRestoreArray(v, &array));
6541   PetscFunctionReturn(0);
6542 }
6543 
6544 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[]) {
6545   PetscMPIInt rank;
6546   PetscInt    i, j;
6547 
6548   PetscFunctionBegin;
6549   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6550   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6551   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6552   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6553   numCIndices = numCIndices ? numCIndices : numRIndices;
6554   if (!values) PetscFunctionReturn(0);
6555   for (i = 0; i < numRIndices; i++) {
6556     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6557     for (j = 0; j < numCIndices; j++) {
6558 #if defined(PETSC_USE_COMPLEX)
6559       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6560 #else
6561       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6562 #endif
6563     }
6564     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6565   }
6566   PetscFunctionReturn(0);
6567 }
6568 
6569 /*
6570   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6571 
6572   Input Parameters:
6573 + section - The section for this data layout
6574 . islocal - Is the section (and thus indices being requested) local or global?
6575 . point   - The point contributing dofs with these indices
6576 . off     - The global offset of this point
6577 . loff    - The local offset of each field
6578 . setBC   - The flag determining whether to include indices of boundary values
6579 . perm    - A permutation of the dofs on this point, or NULL
6580 - indperm - A permutation of the entire indices array, or NULL
6581 
6582   Output Parameter:
6583 . indices - Indices for dofs on this point
6584 
6585   Level: developer
6586 
6587   Note: The indices could be local or global, depending on the value of 'off'.
6588 */
6589 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[]) {
6590   PetscInt        dof;   /* The number of unknowns on this point */
6591   PetscInt        cdof;  /* The number of constraints on this point */
6592   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6593   PetscInt        cind = 0, k;
6594 
6595   PetscFunctionBegin;
6596   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6597   PetscCall(PetscSectionGetDof(section, point, &dof));
6598   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6599   if (!cdof || setBC) {
6600     for (k = 0; k < dof; ++k) {
6601       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6602       const PetscInt ind    = indperm ? indperm[preind] : preind;
6603 
6604       indices[ind] = off + k;
6605     }
6606   } else {
6607     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6608     for (k = 0; k < dof; ++k) {
6609       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6610       const PetscInt ind    = indperm ? indperm[preind] : preind;
6611 
6612       if ((cind < cdof) && (k == cdofs[cind])) {
6613         /* Insert check for returning constrained indices */
6614         indices[ind] = -(off + k + 1);
6615         ++cind;
6616       } else {
6617         indices[ind] = off + k - (islocal ? 0 : cind);
6618       }
6619     }
6620   }
6621   *loff += dof;
6622   PetscFunctionReturn(0);
6623 }
6624 
6625 /*
6626  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6627 
6628  Input Parameters:
6629 + section - a section (global or local)
6630 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6631 . point - point within section
6632 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6633 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6634 . setBC - identify constrained (boundary condition) points via involution.
6635 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6636 . permsoff - offset
6637 - indperm - index permutation
6638 
6639  Output Parameter:
6640 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6641 . indices - array to hold indices (as defined by section) of each dof associated with point
6642 
6643  Notes:
6644  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6645  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6646  in the local vector.
6647 
6648  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6649  significant).  It is invalid to call with a global section and setBC=true.
6650 
6651  Developer Note:
6652  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6653  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6654  offset could be obtained from the section instead of passing it explicitly as we do now.
6655 
6656  Example:
6657  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6658  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6659  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6660  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.
6661 
6662  Level: developer
6663 */
6664 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[]) {
6665   PetscInt numFields, foff, f;
6666 
6667   PetscFunctionBegin;
6668   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6669   PetscCall(PetscSectionGetNumFields(section, &numFields));
6670   for (f = 0, foff = 0; f < numFields; ++f) {
6671     PetscInt        fdof, cfdof;
6672     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6673     PetscInt        cind = 0, b;
6674     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6675 
6676     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6677     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6678     if (!cfdof || setBC) {
6679       for (b = 0; b < fdof; ++b) {
6680         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6681         const PetscInt ind    = indperm ? indperm[preind] : preind;
6682 
6683         indices[ind] = off + foff + b;
6684       }
6685     } else {
6686       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6687       for (b = 0; b < fdof; ++b) {
6688         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6689         const PetscInt ind    = indperm ? indperm[preind] : preind;
6690 
6691         if ((cind < cfdof) && (b == fcdofs[cind])) {
6692           indices[ind] = -(off + foff + b + 1);
6693           ++cind;
6694         } else {
6695           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6696         }
6697       }
6698     }
6699     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6700     foffs[f] += fdof;
6701   }
6702   PetscFunctionReturn(0);
6703 }
6704 
6705 /*
6706   This version believes the globalSection offsets for each field, rather than just the point offset
6707 
6708  . foffs - The offset into 'indices' for each field, since it is segregated by field
6709 
6710  Notes:
6711  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6712  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6713 */
6714 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[]) {
6715   PetscInt numFields, foff, f;
6716 
6717   PetscFunctionBegin;
6718   PetscCall(PetscSectionGetNumFields(section, &numFields));
6719   for (f = 0; f < numFields; ++f) {
6720     PetscInt        fdof, cfdof;
6721     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6722     PetscInt        cind = 0, b;
6723     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6724 
6725     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6726     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6727     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6728     if (!cfdof) {
6729       for (b = 0; b < fdof; ++b) {
6730         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6731         const PetscInt ind    = indperm ? indperm[preind] : preind;
6732 
6733         indices[ind] = foff + b;
6734       }
6735     } else {
6736       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6737       for (b = 0; b < fdof; ++b) {
6738         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6739         const PetscInt ind    = indperm ? indperm[preind] : preind;
6740 
6741         if ((cind < cfdof) && (b == fcdofs[cind])) {
6742           indices[ind] = -(foff + b + 1);
6743           ++cind;
6744         } else {
6745           indices[ind] = foff + b - cind;
6746         }
6747       }
6748     }
6749     foffs[f] += fdof;
6750   }
6751   PetscFunctionReturn(0);
6752 }
6753 
6754 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) {
6755   Mat             cMat;
6756   PetscSection    aSec, cSec;
6757   IS              aIS;
6758   PetscInt        aStart = -1, aEnd = -1;
6759   const PetscInt *anchors;
6760   PetscInt        numFields, f, p, q, newP = 0;
6761   PetscInt        newNumPoints = 0, newNumIndices = 0;
6762   PetscInt       *newPoints, *indices, *newIndices;
6763   PetscInt        maxAnchor, maxDof;
6764   PetscInt        newOffsets[32];
6765   PetscInt       *pointMatOffsets[32];
6766   PetscInt       *newPointOffsets[32];
6767   PetscScalar    *pointMat[32];
6768   PetscScalar    *newValues      = NULL, *tmpValues;
6769   PetscBool       anyConstrained = PETSC_FALSE;
6770 
6771   PetscFunctionBegin;
6772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6773   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6774   PetscCall(PetscSectionGetNumFields(section, &numFields));
6775 
6776   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
6777   /* if there are point-to-point constraints */
6778   if (aSec) {
6779     PetscCall(PetscArrayzero(newOffsets, 32));
6780     PetscCall(ISGetIndices(aIS, &anchors));
6781     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
6782     /* figure out how many points are going to be in the new element matrix
6783      * (we allow double counting, because it's all just going to be summed
6784      * into the global matrix anyway) */
6785     for (p = 0; p < 2 * numPoints; p += 2) {
6786       PetscInt b    = points[p];
6787       PetscInt bDof = 0, bSecDof;
6788 
6789       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6790       if (!bSecDof) continue;
6791       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6792       if (bDof) {
6793         /* this point is constrained */
6794         /* it is going to be replaced by its anchors */
6795         PetscInt bOff, q;
6796 
6797         anyConstrained = PETSC_TRUE;
6798         newNumPoints += bDof;
6799         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6800         for (q = 0; q < bDof; q++) {
6801           PetscInt a = anchors[bOff + q];
6802           PetscInt aDof;
6803 
6804           PetscCall(PetscSectionGetDof(section, a, &aDof));
6805           newNumIndices += aDof;
6806           for (f = 0; f < numFields; ++f) {
6807             PetscInt fDof;
6808 
6809             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6810             newOffsets[f + 1] += fDof;
6811           }
6812         }
6813       } else {
6814         /* this point is not constrained */
6815         newNumPoints++;
6816         newNumIndices += bSecDof;
6817         for (f = 0; f < numFields; ++f) {
6818           PetscInt fDof;
6819 
6820           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6821           newOffsets[f + 1] += fDof;
6822         }
6823       }
6824     }
6825   }
6826   if (!anyConstrained) {
6827     if (outNumPoints) *outNumPoints = 0;
6828     if (outNumIndices) *outNumIndices = 0;
6829     if (outPoints) *outPoints = NULL;
6830     if (outValues) *outValues = NULL;
6831     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6832     PetscFunctionReturn(0);
6833   }
6834 
6835   if (outNumPoints) *outNumPoints = newNumPoints;
6836   if (outNumIndices) *outNumIndices = newNumIndices;
6837 
6838   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
6839 
6840   if (!outPoints && !outValues) {
6841     if (offsets) {
6842       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
6843     }
6844     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6845     PetscFunctionReturn(0);
6846   }
6847 
6848   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6849 
6850   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6851 
6852   /* workspaces */
6853   if (numFields) {
6854     for (f = 0; f < numFields; f++) {
6855       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
6856       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
6857     }
6858   } else {
6859     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
6860     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
6861   }
6862 
6863   /* get workspaces for the point-to-point matrices */
6864   if (numFields) {
6865     PetscInt totalOffset, totalMatOffset;
6866 
6867     for (p = 0; p < numPoints; p++) {
6868       PetscInt b    = points[2 * p];
6869       PetscInt bDof = 0, bSecDof;
6870 
6871       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6872       if (!bSecDof) {
6873         for (f = 0; f < numFields; f++) {
6874           newPointOffsets[f][p + 1] = 0;
6875           pointMatOffsets[f][p + 1] = 0;
6876         }
6877         continue;
6878       }
6879       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6880       if (bDof) {
6881         for (f = 0; f < numFields; f++) {
6882           PetscInt fDof, q, bOff, allFDof = 0;
6883 
6884           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6885           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6886           for (q = 0; q < bDof; q++) {
6887             PetscInt a = anchors[bOff + q];
6888             PetscInt aFDof;
6889 
6890             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6891             allFDof += aFDof;
6892           }
6893           newPointOffsets[f][p + 1] = allFDof;
6894           pointMatOffsets[f][p + 1] = fDof * allFDof;
6895         }
6896       } else {
6897         for (f = 0; f < numFields; f++) {
6898           PetscInt fDof;
6899 
6900           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6901           newPointOffsets[f][p + 1] = fDof;
6902           pointMatOffsets[f][p + 1] = 0;
6903         }
6904       }
6905     }
6906     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6907       newPointOffsets[f][0] = totalOffset;
6908       pointMatOffsets[f][0] = totalMatOffset;
6909       for (p = 0; p < numPoints; p++) {
6910         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
6911         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
6912       }
6913       totalOffset    = newPointOffsets[f][numPoints];
6914       totalMatOffset = pointMatOffsets[f][numPoints];
6915       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
6916     }
6917   } else {
6918     for (p = 0; p < numPoints; p++) {
6919       PetscInt b    = points[2 * p];
6920       PetscInt bDof = 0, bSecDof;
6921 
6922       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6923       if (!bSecDof) {
6924         newPointOffsets[0][p + 1] = 0;
6925         pointMatOffsets[0][p + 1] = 0;
6926         continue;
6927       }
6928       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6929       if (bDof) {
6930         PetscInt bOff, q, allDof = 0;
6931 
6932         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6933         for (q = 0; q < bDof; q++) {
6934           PetscInt a = anchors[bOff + q], aDof;
6935 
6936           PetscCall(PetscSectionGetDof(section, a, &aDof));
6937           allDof += aDof;
6938         }
6939         newPointOffsets[0][p + 1] = allDof;
6940         pointMatOffsets[0][p + 1] = bSecDof * allDof;
6941       } else {
6942         newPointOffsets[0][p + 1] = bSecDof;
6943         pointMatOffsets[0][p + 1] = 0;
6944       }
6945     }
6946     newPointOffsets[0][0] = 0;
6947     pointMatOffsets[0][0] = 0;
6948     for (p = 0; p < numPoints; p++) {
6949       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
6950       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
6951     }
6952     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
6953   }
6954 
6955   /* output arrays */
6956   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
6957 
6958   /* get the point-to-point matrices; construct newPoints */
6959   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6960   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6961   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
6962   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
6963   if (numFields) {
6964     for (p = 0, newP = 0; p < numPoints; p++) {
6965       PetscInt b    = points[2 * p];
6966       PetscInt o    = points[2 * p + 1];
6967       PetscInt bDof = 0, bSecDof;
6968 
6969       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6970       if (!bSecDof) continue;
6971       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6972       if (bDof) {
6973         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6974 
6975         fStart[0] = 0;
6976         fEnd[0]   = 0;
6977         for (f = 0; f < numFields; f++) {
6978           PetscInt fDof;
6979 
6980           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6981           fStart[f + 1] = fStart[f] + fDof;
6982           fEnd[f + 1]   = fStart[f + 1];
6983         }
6984         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
6985         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
6986 
6987         fAnchorStart[0] = 0;
6988         fAnchorEnd[0]   = 0;
6989         for (f = 0; f < numFields; f++) {
6990           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
6991 
6992           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
6993           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
6994         }
6995         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6996         for (q = 0; q < bDof; q++) {
6997           PetscInt a = anchors[bOff + q], aOff;
6998 
6999           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7000           newPoints[2 * (newP + q)]     = a;
7001           newPoints[2 * (newP + q) + 1] = 0;
7002           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7003           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7004         }
7005         newP += bDof;
7006 
7007         if (outValues) {
7008           /* get the point-to-point submatrix */
7009           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]));
7010         }
7011       } else {
7012         newPoints[2 * newP]     = b;
7013         newPoints[2 * newP + 1] = o;
7014         newP++;
7015       }
7016     }
7017   } else {
7018     for (p = 0; p < numPoints; p++) {
7019       PetscInt b    = points[2 * p];
7020       PetscInt o    = points[2 * p + 1];
7021       PetscInt bDof = 0, bSecDof;
7022 
7023       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7024       if (!bSecDof) continue;
7025       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7026       if (bDof) {
7027         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7028 
7029         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7030         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7031 
7032         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7033         for (q = 0; q < bDof; q++) {
7034           PetscInt a = anchors[bOff + q], aOff;
7035 
7036           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7037 
7038           newPoints[2 * (newP + q)]     = a;
7039           newPoints[2 * (newP + q) + 1] = 0;
7040           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7041           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7042         }
7043         newP += bDof;
7044 
7045         /* get the point-to-point submatrix */
7046         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7047       } else {
7048         newPoints[2 * newP]     = b;
7049         newPoints[2 * newP + 1] = o;
7050         newP++;
7051       }
7052     }
7053   }
7054 
7055   if (outValues) {
7056     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7057     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7058     /* multiply constraints on the right */
7059     if (numFields) {
7060       for (f = 0; f < numFields; f++) {
7061         PetscInt oldOff = offsets[f];
7062 
7063         for (p = 0; p < numPoints; p++) {
7064           PetscInt cStart = newPointOffsets[f][p];
7065           PetscInt b      = points[2 * p];
7066           PetscInt c, r, k;
7067           PetscInt dof;
7068 
7069           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7070           if (!dof) continue;
7071           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7072             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7073             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7074 
7075             for (r = 0; r < numIndices; r++) {
7076               for (c = 0; c < nCols; c++) {
7077                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7078               }
7079             }
7080           } else {
7081             /* copy this column as is */
7082             for (r = 0; r < numIndices; r++) {
7083               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7084             }
7085           }
7086           oldOff += dof;
7087         }
7088       }
7089     } else {
7090       PetscInt oldOff = 0;
7091       for (p = 0; p < numPoints; p++) {
7092         PetscInt cStart = newPointOffsets[0][p];
7093         PetscInt b      = points[2 * p];
7094         PetscInt c, r, k;
7095         PetscInt dof;
7096 
7097         PetscCall(PetscSectionGetDof(section, b, &dof));
7098         if (!dof) continue;
7099         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7100           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7101           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7102 
7103           for (r = 0; r < numIndices; r++) {
7104             for (c = 0; c < nCols; c++) {
7105               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7106             }
7107           }
7108         } else {
7109           /* copy this column as is */
7110           for (r = 0; r < numIndices; r++) {
7111             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7112           }
7113         }
7114         oldOff += dof;
7115       }
7116     }
7117 
7118     if (multiplyLeft) {
7119       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7120       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7121       /* multiply constraints transpose on the left */
7122       if (numFields) {
7123         for (f = 0; f < numFields; f++) {
7124           PetscInt oldOff = offsets[f];
7125 
7126           for (p = 0; p < numPoints; p++) {
7127             PetscInt rStart = newPointOffsets[f][p];
7128             PetscInt b      = points[2 * p];
7129             PetscInt c, r, k;
7130             PetscInt dof;
7131 
7132             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7133             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7134               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7135               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7136 
7137               for (r = 0; r < nRows; r++) {
7138                 for (c = 0; c < newNumIndices; c++) {
7139                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7140                 }
7141               }
7142             } else {
7143               /* copy this row as is */
7144               for (r = 0; r < dof; r++) {
7145                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7146               }
7147             }
7148             oldOff += dof;
7149           }
7150         }
7151       } else {
7152         PetscInt oldOff = 0;
7153 
7154         for (p = 0; p < numPoints; p++) {
7155           PetscInt rStart = newPointOffsets[0][p];
7156           PetscInt b      = points[2 * p];
7157           PetscInt c, r, k;
7158           PetscInt dof;
7159 
7160           PetscCall(PetscSectionGetDof(section, b, &dof));
7161           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7162             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7163             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7164 
7165             for (r = 0; r < nRows; r++) {
7166               for (c = 0; c < newNumIndices; c++) {
7167                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7168               }
7169             }
7170           } else {
7171             /* copy this row as is */
7172             for (r = 0; r < dof; r++) {
7173               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7174             }
7175           }
7176           oldOff += dof;
7177         }
7178       }
7179 
7180       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7181     } else {
7182       newValues = tmpValues;
7183     }
7184   }
7185 
7186   /* clean up */
7187   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7188   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7189 
7190   if (numFields) {
7191     for (f = 0; f < numFields; f++) {
7192       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7193       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7194       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7195     }
7196   } else {
7197     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7198     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7199     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7200   }
7201   PetscCall(ISRestoreIndices(aIS, &anchors));
7202 
7203   /* output */
7204   if (outPoints) {
7205     *outPoints = newPoints;
7206   } else {
7207     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7208   }
7209   if (outValues) *outValues = newValues;
7210   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7211   PetscFunctionReturn(0);
7212 }
7213 
7214 /*@C
7215   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7216 
7217   Not collective
7218 
7219   Input Parameters:
7220 + dm         - The DM
7221 . section    - The PetscSection describing the points (a local section)
7222 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7223 . point      - The point defining the closure
7224 - useClPerm  - Use the closure point permutation if available
7225 
7226   Output Parameters:
7227 + numIndices - The number of dof indices in the closure of point with the input sections
7228 . indices    - The dof indices
7229 . outOffsets - Array to write the field offsets into, or NULL
7230 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7231 
7232   Notes:
7233   Must call DMPlexRestoreClosureIndices() to free allocated memory
7234 
7235   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7236   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7237   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7238   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7239   indices (with the above semantics) are implied.
7240 
7241   Level: advanced
7242 
7243 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7244 @*/
7245 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7246   /* Closure ordering */
7247   PetscSection        clSection;
7248   IS                  clPoints;
7249   const PetscInt     *clp;
7250   PetscInt           *points;
7251   const PetscInt     *clperm    = NULL;
7252   /* Dof permutation and sign flips */
7253   const PetscInt    **perms[32] = {NULL};
7254   const PetscScalar **flips[32] = {NULL};
7255   PetscScalar        *valCopy   = NULL;
7256   /* Hanging node constraints */
7257   PetscInt           *pointsC   = NULL;
7258   PetscScalar        *valuesC   = NULL;
7259   PetscInt            NclC, NiC;
7260 
7261   PetscInt *idx;
7262   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7263   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7264 
7265   PetscFunctionBeginHot;
7266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7267   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7268   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7269   if (numIndices) PetscValidIntPointer(numIndices, 6);
7270   if (indices) PetscValidPointer(indices, 7);
7271   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7272   if (values) PetscValidPointer(values, 9);
7273   PetscCall(PetscSectionGetNumFields(section, &Nf));
7274   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7275   PetscCall(PetscArrayzero(offsets, 32));
7276   /* 1) Get points in closure */
7277   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7278   if (useClPerm) {
7279     PetscInt depth, clsize;
7280     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7281     for (clsize = 0, p = 0; p < Ncl; p++) {
7282       PetscInt dof;
7283       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7284       clsize += dof;
7285     }
7286     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7287   }
7288   /* 2) Get number of indices on these points and field offsets from section */
7289   for (p = 0; p < Ncl * 2; p += 2) {
7290     PetscInt dof, fdof;
7291 
7292     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7293     for (f = 0; f < Nf; ++f) {
7294       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7295       offsets[f + 1] += fdof;
7296     }
7297     Ni += dof;
7298   }
7299   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7300   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7301   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7302   for (f = 0; f < PetscMax(1, Nf); ++f) {
7303     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7304     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7305     /* may need to apply sign changes to the element matrix */
7306     if (values && flips[f]) {
7307       PetscInt foffset = offsets[f];
7308 
7309       for (p = 0; p < Ncl; ++p) {
7310         PetscInt           pnt  = points[2 * p], fdof;
7311         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7312 
7313         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7314         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7315         if (flip) {
7316           PetscInt i, j, k;
7317 
7318           if (!valCopy) {
7319             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7320             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7321             *values = valCopy;
7322           }
7323           for (i = 0; i < fdof; ++i) {
7324             PetscScalar fval = flip[i];
7325 
7326             for (k = 0; k < Ni; ++k) {
7327               valCopy[Ni * (foffset + i) + k] *= fval;
7328               valCopy[Ni * k + (foffset + i)] *= fval;
7329             }
7330           }
7331         }
7332         foffset += fdof;
7333       }
7334     }
7335   }
7336   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7337   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7338   if (NclC) {
7339     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7340     for (f = 0; f < PetscMax(1, Nf); ++f) {
7341       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7342       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7343     }
7344     for (f = 0; f < PetscMax(1, Nf); ++f) {
7345       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7346       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7347     }
7348     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7349     Ncl    = NclC;
7350     Ni     = NiC;
7351     points = pointsC;
7352     if (values) *values = valuesC;
7353   }
7354   /* 5) Calculate indices */
7355   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7356   if (Nf) {
7357     PetscInt  idxOff;
7358     PetscBool useFieldOffsets;
7359 
7360     if (outOffsets) {
7361       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7362     }
7363     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7364     if (useFieldOffsets) {
7365       for (p = 0; p < Ncl; ++p) {
7366         const PetscInt pnt = points[p * 2];
7367 
7368         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7369       }
7370     } else {
7371       for (p = 0; p < Ncl; ++p) {
7372         const PetscInt pnt = points[p * 2];
7373 
7374         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7375         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7376          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7377          * global section. */
7378         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7379       }
7380     }
7381   } else {
7382     PetscInt off = 0, idxOff;
7383 
7384     for (p = 0; p < Ncl; ++p) {
7385       const PetscInt  pnt  = points[p * 2];
7386       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7387 
7388       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7389       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7390        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7391       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7392     }
7393   }
7394   /* 6) Cleanup */
7395   for (f = 0; f < PetscMax(1, Nf); ++f) {
7396     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7397     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7398   }
7399   if (NclC) {
7400     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7401   } else {
7402     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7403   }
7404 
7405   if (numIndices) *numIndices = Ni;
7406   if (indices) *indices = idx;
7407   PetscFunctionReturn(0);
7408 }
7409 
7410 /*@C
7411   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7412 
7413   Not collective
7414 
7415   Input Parameters:
7416 + dm         - The DM
7417 . section    - The PetscSection describing the points (a local section)
7418 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7419 . point      - The point defining the closure
7420 - useClPerm  - Use the closure point permutation if available
7421 
7422   Output Parameters:
7423 + numIndices - The number of dof indices in the closure of point with the input sections
7424 . indices    - The dof indices
7425 . outOffsets - Array to write the field offsets into, or NULL
7426 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7427 
7428   Notes:
7429   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7430 
7431   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7432   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7433   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7434   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7435   indices (with the above semantics) are implied.
7436 
7437   Level: advanced
7438 
7439 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7440 @*/
7441 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7442   PetscFunctionBegin;
7443   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7444   PetscValidPointer(indices, 7);
7445   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7446   PetscFunctionReturn(0);
7447 }
7448 
7449 /*@C
7450   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7451 
7452   Not collective
7453 
7454   Input Parameters:
7455 + dm - The DM
7456 . section - The section describing the layout in v, or NULL to use the default section
7457 . globalSection - The section describing the layout in v, or NULL to use the default global section
7458 . A - The matrix
7459 . point - The point in the DM
7460 . values - The array of values
7461 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7462 
7463   Fortran Notes:
7464   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7465 
7466   Level: intermediate
7467 
7468 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7469 @*/
7470 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7471   DM_Plex           *mesh = (DM_Plex *)dm->data;
7472   PetscInt          *indices;
7473   PetscInt           numIndices;
7474   const PetscScalar *valuesOrig = values;
7475   PetscErrorCode     ierr;
7476 
7477   PetscFunctionBegin;
7478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7479   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7480   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7481   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7482   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7483   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7484 
7485   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7486 
7487   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7488   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7489   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7490   if (ierr) {
7491     PetscMPIInt rank;
7492 
7493     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7494     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7495     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7496     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7497     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7498     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7499   }
7500   if (mesh->printFEM > 1) {
7501     PetscInt i;
7502     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7503     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7504     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7505   }
7506 
7507   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7508   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7509   PetscFunctionReturn(0);
7510 }
7511 
7512 /*@C
7513   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7514 
7515   Not collective
7516 
7517   Input Parameters:
7518 + dmRow - The DM for the row fields
7519 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7520 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7521 . dmCol - The DM for the column fields
7522 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7523 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7524 . A - The matrix
7525 . point - The point in the DMs
7526 . values - The array of values
7527 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7528 
7529   Level: intermediate
7530 
7531 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7532 @*/
7533 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7534   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7535   PetscInt          *indicesRow, *indicesCol;
7536   PetscInt           numIndicesRow, numIndicesCol;
7537   const PetscScalar *valuesOrig = values;
7538   PetscErrorCode     ierr;
7539 
7540   PetscFunctionBegin;
7541   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7542   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7543   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7544   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7545   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7546   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7547   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7548   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7549   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7550   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7551   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7552 
7553   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7554   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7555 
7556   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7557   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7558   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7559   if (ierr) {
7560     PetscMPIInt rank;
7561 
7562     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7563     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7564     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7565     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7566     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7567     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7568   }
7569 
7570   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7571   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7572   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7573   PetscFunctionReturn(0);
7574 }
7575 
7576 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7577   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7578   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7579   PetscInt       *cpoints = NULL;
7580   PetscInt       *findices, *cindices;
7581   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7582   PetscInt        foffsets[32], coffsets[32];
7583   DMPolytopeType  ct;
7584   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7585   PetscErrorCode  ierr;
7586 
7587   PetscFunctionBegin;
7588   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7589   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7590   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7591   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7592   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7593   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7594   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7595   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7596   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7597   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7598   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7599   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7600   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7601   PetscCall(PetscArrayzero(foffsets, 32));
7602   PetscCall(PetscArrayzero(coffsets, 32));
7603   /* Column indices */
7604   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7605   maxFPoints = numCPoints;
7606   /* Compress out points not in the section */
7607   /*   TODO: Squeeze out points with 0 dof as well */
7608   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7609   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7610     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7611       cpoints[q * 2]     = cpoints[p];
7612       cpoints[q * 2 + 1] = cpoints[p + 1];
7613       ++q;
7614     }
7615   }
7616   numCPoints = q;
7617   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7618     PetscInt fdof;
7619 
7620     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7621     if (!dof) continue;
7622     for (f = 0; f < numFields; ++f) {
7623       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7624       coffsets[f + 1] += fdof;
7625     }
7626     numCIndices += dof;
7627   }
7628   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7629   /* Row indices */
7630   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7631   {
7632     DMPlexTransform tr;
7633     DMPolytopeType *rct;
7634     PetscInt       *rsize, *rcone, *rornt, Nt;
7635 
7636     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7637     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7638     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7639     numSubcells = rsize[Nt - 1];
7640     PetscCall(DMPlexTransformDestroy(&tr));
7641   }
7642   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7643   for (r = 0, q = 0; r < numSubcells; ++r) {
7644     /* TODO Map from coarse to fine cells */
7645     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7646     /* Compress out points not in the section */
7647     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7648     for (p = 0; p < numFPoints * 2; p += 2) {
7649       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7650         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7651         if (!dof) continue;
7652         for (s = 0; s < q; ++s)
7653           if (fpoints[p] == ftotpoints[s * 2]) break;
7654         if (s < q) continue;
7655         ftotpoints[q * 2]     = fpoints[p];
7656         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7657         ++q;
7658       }
7659     }
7660     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7661   }
7662   numFPoints = q;
7663   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7664     PetscInt fdof;
7665 
7666     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7667     if (!dof) continue;
7668     for (f = 0; f < numFields; ++f) {
7669       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7670       foffsets[f + 1] += fdof;
7671     }
7672     numFIndices += dof;
7673   }
7674   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7675 
7676   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7677   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7678   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7679   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7680   if (numFields) {
7681     const PetscInt **permsF[32] = {NULL};
7682     const PetscInt **permsC[32] = {NULL};
7683 
7684     for (f = 0; f < numFields; f++) {
7685       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7686       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7687     }
7688     for (p = 0; p < numFPoints; p++) {
7689       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7690       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7691     }
7692     for (p = 0; p < numCPoints; p++) {
7693       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7694       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7695     }
7696     for (f = 0; f < numFields; f++) {
7697       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7698       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7699     }
7700   } else {
7701     const PetscInt **permsF = NULL;
7702     const PetscInt **permsC = NULL;
7703 
7704     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7705     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7706     for (p = 0, off = 0; p < numFPoints; p++) {
7707       const PetscInt *perm = permsF ? permsF[p] : NULL;
7708 
7709       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7710       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7711     }
7712     for (p = 0, off = 0; p < numCPoints; p++) {
7713       const PetscInt *perm = permsC ? permsC[p] : NULL;
7714 
7715       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7716       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7717     }
7718     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7719     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7720   }
7721   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7722   /* TODO: flips */
7723   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7724   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7725   if (ierr) {
7726     PetscMPIInt rank;
7727 
7728     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7729     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7730     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7731     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7732     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7733   }
7734   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7735   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7736   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7737   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7738   PetscFunctionReturn(0);
7739 }
7740 
7741 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[]) {
7742   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7743   PetscInt       *cpoints = NULL;
7744   PetscInt        foffsets[32], coffsets[32];
7745   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7746   DMPolytopeType  ct;
7747   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7748 
7749   PetscFunctionBegin;
7750   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7751   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7752   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7753   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7754   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7755   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7756   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7757   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7758   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7759   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7760   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7761   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7762   PetscCall(PetscArrayzero(foffsets, 32));
7763   PetscCall(PetscArrayzero(coffsets, 32));
7764   /* Column indices */
7765   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7766   maxFPoints = numCPoints;
7767   /* Compress out points not in the section */
7768   /*   TODO: Squeeze out points with 0 dof as well */
7769   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7770   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7771     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7772       cpoints[q * 2]     = cpoints[p];
7773       cpoints[q * 2 + 1] = cpoints[p + 1];
7774       ++q;
7775     }
7776   }
7777   numCPoints = q;
7778   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7779     PetscInt fdof;
7780 
7781     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7782     if (!dof) continue;
7783     for (f = 0; f < numFields; ++f) {
7784       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7785       coffsets[f + 1] += fdof;
7786     }
7787     numCIndices += dof;
7788   }
7789   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7790   /* Row indices */
7791   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7792   {
7793     DMPlexTransform tr;
7794     DMPolytopeType *rct;
7795     PetscInt       *rsize, *rcone, *rornt, Nt;
7796 
7797     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7798     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7799     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7800     numSubcells = rsize[Nt - 1];
7801     PetscCall(DMPlexTransformDestroy(&tr));
7802   }
7803   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7804   for (r = 0, q = 0; r < numSubcells; ++r) {
7805     /* TODO Map from coarse to fine cells */
7806     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7807     /* Compress out points not in the section */
7808     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7809     for (p = 0; p < numFPoints * 2; p += 2) {
7810       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7811         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7812         if (!dof) continue;
7813         for (s = 0; s < q; ++s)
7814           if (fpoints[p] == ftotpoints[s * 2]) break;
7815         if (s < q) continue;
7816         ftotpoints[q * 2]     = fpoints[p];
7817         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7818         ++q;
7819       }
7820     }
7821     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7822   }
7823   numFPoints = q;
7824   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7825     PetscInt fdof;
7826 
7827     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7828     if (!dof) continue;
7829     for (f = 0; f < numFields; ++f) {
7830       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7831       foffsets[f + 1] += fdof;
7832     }
7833     numFIndices += dof;
7834   }
7835   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7836 
7837   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7838   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7839   if (numFields) {
7840     const PetscInt **permsF[32] = {NULL};
7841     const PetscInt **permsC[32] = {NULL};
7842 
7843     for (f = 0; f < numFields; f++) {
7844       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7845       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7846     }
7847     for (p = 0; p < numFPoints; p++) {
7848       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7849       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7850     }
7851     for (p = 0; p < numCPoints; p++) {
7852       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7853       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7854     }
7855     for (f = 0; f < numFields; f++) {
7856       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7857       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7858     }
7859   } else {
7860     const PetscInt **permsF = NULL;
7861     const PetscInt **permsC = NULL;
7862 
7863     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7864     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7865     for (p = 0, off = 0; p < numFPoints; p++) {
7866       const PetscInt *perm = permsF ? permsF[p] : NULL;
7867 
7868       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7869       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7870     }
7871     for (p = 0, off = 0; p < numCPoints; p++) {
7872       const PetscInt *perm = permsC ? permsC[p] : NULL;
7873 
7874       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7875       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7876     }
7877     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7878     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7879   }
7880   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7881   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7882   PetscFunctionReturn(0);
7883 }
7884 
7885 /*@C
7886   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7887 
7888   Input Parameter:
7889 . dm   - The DMPlex object
7890 
7891   Output Parameter:
7892 . cellHeight - The height of a cell
7893 
7894   Level: developer
7895 
7896 .seealso `DMPlexSetVTKCellHeight()`
7897 @*/
7898 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight) {
7899   DM_Plex *mesh = (DM_Plex *)dm->data;
7900 
7901   PetscFunctionBegin;
7902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7903   PetscValidIntPointer(cellHeight, 2);
7904   *cellHeight = mesh->vtkCellHeight;
7905   PetscFunctionReturn(0);
7906 }
7907 
7908 /*@C
7909   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7910 
7911   Input Parameters:
7912 + dm   - The DMPlex object
7913 - cellHeight - The height of a cell
7914 
7915   Level: developer
7916 
7917 .seealso `DMPlexGetVTKCellHeight()`
7918 @*/
7919 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight) {
7920   DM_Plex *mesh = (DM_Plex *)dm->data;
7921 
7922   PetscFunctionBegin;
7923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7924   mesh->vtkCellHeight = cellHeight;
7925   PetscFunctionReturn(0);
7926 }
7927 
7928 /*@
7929   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7930 
7931   Input Parameter:
7932 . dm - The DMPlex object
7933 
7934   Output Parameters:
7935 + gcStart - The first ghost cell, or NULL
7936 - gcEnd   - The upper bound on ghost cells, or NULL
7937 
7938   Level: advanced
7939 
7940 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
7941 @*/
7942 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd) {
7943   DMLabel ctLabel;
7944 
7945   PetscFunctionBegin;
7946   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7947   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
7948   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
7949   // Reset label for fast lookup
7950   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
7951   PetscFunctionReturn(0);
7952 }
7953 
7954 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering) {
7955   PetscSection section, globalSection;
7956   PetscInt    *numbers, p;
7957 
7958   PetscFunctionBegin;
7959   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
7960   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
7961   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
7962   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
7963   PetscCall(PetscSectionSetUp(section));
7964   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
7965   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
7966   for (p = pStart; p < pEnd; ++p) {
7967     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
7968     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
7969     else numbers[p - pStart] += shift;
7970   }
7971   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
7972   if (globalSize) {
7973     PetscLayout layout;
7974     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
7975     PetscCall(PetscLayoutGetSize(layout, globalSize));
7976     PetscCall(PetscLayoutDestroy(&layout));
7977   }
7978   PetscCall(PetscSectionDestroy(&section));
7979   PetscCall(PetscSectionDestroy(&globalSection));
7980   PetscFunctionReturn(0);
7981 }
7982 
7983 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers) {
7984   PetscInt cellHeight, cStart, cEnd;
7985 
7986   PetscFunctionBegin;
7987   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
7988   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
7989   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
7990   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
7991   PetscFunctionReturn(0);
7992 }
7993 
7994 /*@
7995   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
7996 
7997   Input Parameter:
7998 . dm   - The DMPlex object
7999 
8000   Output Parameter:
8001 . globalCellNumbers - Global cell numbers for all cells on this process
8002 
8003   Level: developer
8004 
8005 .seealso `DMPlexGetVertexNumbering()`
8006 @*/
8007 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers) {
8008   DM_Plex *mesh = (DM_Plex *)dm->data;
8009 
8010   PetscFunctionBegin;
8011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8012   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8013   *globalCellNumbers = mesh->globalCellNumbers;
8014   PetscFunctionReturn(0);
8015 }
8016 
8017 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers) {
8018   PetscInt vStart, vEnd;
8019 
8020   PetscFunctionBegin;
8021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8022   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8023   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8024   PetscFunctionReturn(0);
8025 }
8026 
8027 /*@
8028   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8029 
8030   Input Parameter:
8031 . dm   - The DMPlex object
8032 
8033   Output Parameter:
8034 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8035 
8036   Level: developer
8037 
8038 .seealso `DMPlexGetCellNumbering()`
8039 @*/
8040 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers) {
8041   DM_Plex *mesh = (DM_Plex *)dm->data;
8042 
8043   PetscFunctionBegin;
8044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8045   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8046   *globalVertexNumbers = mesh->globalVertexNumbers;
8047   PetscFunctionReturn(0);
8048 }
8049 
8050 /*@
8051   DMPlexCreatePointNumbering - Create a global numbering for all points.
8052 
8053   Collective on dm
8054 
8055   Input Parameter:
8056 . dm   - The DMPlex object
8057 
8058   Output Parameter:
8059 . globalPointNumbers - Global numbers for all points on this process
8060 
8061   Notes:
8062 
8063   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8064   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8065   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8066   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8067 
8068   The partitioned mesh is
8069 ```
8070  (2)--0--(3)--1--(4)    (1)--0--(2)
8071 ```
8072   and its global numbering is
8073 ```
8074   (3)--0--(4)--1--(5)--2--(6)
8075 ```
8076   Then the global numbering is provided as
8077 ```
8078 [0] Number of indices in set 5
8079 [0] 0 0
8080 [0] 1 1
8081 [0] 2 3
8082 [0] 3 4
8083 [0] 4 -6
8084 [1] Number of indices in set 3
8085 [1] 0 2
8086 [1] 1 5
8087 [1] 2 6
8088 ```
8089 
8090   Level: developer
8091 
8092 .seealso `DMPlexGetCellNumbering()`
8093 @*/
8094 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers) {
8095   IS        nums[4];
8096   PetscInt  depths[4], gdepths[4], starts[4];
8097   PetscInt  depth, d, shift = 0;
8098   PetscBool empty = PETSC_FALSE;
8099 
8100   PetscFunctionBegin;
8101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8102   PetscCall(DMPlexGetDepth(dm, &depth));
8103   // For unstratified meshes use dim instead of depth
8104   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8105   // If any stratum is empty, we must mark all empty
8106   for (d = 0; d <= depth; ++d) {
8107     PetscInt end;
8108 
8109     depths[d] = depth - d;
8110     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8111     if (!(starts[d] - end)) empty = PETSC_TRUE;
8112   }
8113   if (empty)
8114     for (d = 0; d <= depth; ++d) {
8115       depths[d] = -1;
8116       starts[d] = -1;
8117     }
8118   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8119   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8120   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]);
8121   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8122   for (d = 0; d <= depth; ++d) {
8123     PetscInt pStart, pEnd, gsize;
8124 
8125     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8126     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8127     shift += gsize;
8128   }
8129   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8130   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8131   PetscFunctionReturn(0);
8132 }
8133 
8134 /*@
8135   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8136 
8137   Input Parameter:
8138 . dm - The DMPlex object
8139 
8140   Output Parameter:
8141 . ranks - The rank field
8142 
8143   Options Database Keys:
8144 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8145 
8146   Level: intermediate
8147 
8148 .seealso: `DMView()`
8149 @*/
8150 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks) {
8151   DM             rdm;
8152   PetscFE        fe;
8153   PetscScalar   *r;
8154   PetscMPIInt    rank;
8155   DMPolytopeType ct;
8156   PetscInt       dim, cStart, cEnd, c;
8157   PetscBool      simplex;
8158 
8159   PetscFunctionBeginUser;
8160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8161   PetscValidPointer(ranks, 2);
8162   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8163   PetscCall(DMClone(dm, &rdm));
8164   PetscCall(DMGetDimension(rdm, &dim));
8165   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8166   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8167   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8168   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8169   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8170   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8171   PetscCall(PetscFEDestroy(&fe));
8172   PetscCall(DMCreateDS(rdm));
8173   PetscCall(DMCreateGlobalVector(rdm, ranks));
8174   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8175   PetscCall(VecGetArray(*ranks, &r));
8176   for (c = cStart; c < cEnd; ++c) {
8177     PetscScalar *lr;
8178 
8179     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8180     if (lr) *lr = rank;
8181   }
8182   PetscCall(VecRestoreArray(*ranks, &r));
8183   PetscCall(DMDestroy(&rdm));
8184   PetscFunctionReturn(0);
8185 }
8186 
8187 /*@
8188   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8189 
8190   Input Parameters:
8191 + dm    - The DMPlex
8192 - label - The DMLabel
8193 
8194   Output Parameter:
8195 . val - The label value field
8196 
8197   Options Database Keys:
8198 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8199 
8200   Level: intermediate
8201 
8202 .seealso: `DMView()`
8203 @*/
8204 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val) {
8205   DM           rdm;
8206   PetscFE      fe;
8207   PetscScalar *v;
8208   PetscInt     dim, cStart, cEnd, c;
8209 
8210   PetscFunctionBeginUser;
8211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8212   PetscValidPointer(label, 2);
8213   PetscValidPointer(val, 3);
8214   PetscCall(DMClone(dm, &rdm));
8215   PetscCall(DMGetDimension(rdm, &dim));
8216   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8217   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8218   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8219   PetscCall(PetscFEDestroy(&fe));
8220   PetscCall(DMCreateDS(rdm));
8221   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8222   PetscCall(DMCreateGlobalVector(rdm, val));
8223   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8224   PetscCall(VecGetArray(*val, &v));
8225   for (c = cStart; c < cEnd; ++c) {
8226     PetscScalar *lv;
8227     PetscInt     cval;
8228 
8229     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8230     PetscCall(DMLabelGetValue(label, c, &cval));
8231     *lv = cval;
8232   }
8233   PetscCall(VecRestoreArray(*val, &v));
8234   PetscCall(DMDestroy(&rdm));
8235   PetscFunctionReturn(0);
8236 }
8237 
8238 /*@
8239   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8240 
8241   Input Parameter:
8242 . dm - The DMPlex object
8243 
8244   Notes:
8245   This is a useful diagnostic when creating meshes programmatically.
8246 
8247   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8248 
8249   Level: developer
8250 
8251 .seealso: `DMCreate()`, `DMSetFromOptions()`
8252 @*/
8253 PetscErrorCode DMPlexCheckSymmetry(DM dm) {
8254   PetscSection    coneSection, supportSection;
8255   const PetscInt *cone, *support;
8256   PetscInt        coneSize, c, supportSize, s;
8257   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8258   PetscBool       storagecheck = PETSC_TRUE;
8259 
8260   PetscFunctionBegin;
8261   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8262   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8263   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8264   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8265   /* Check that point p is found in the support of its cone points, and vice versa */
8266   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8267   for (p = pStart; p < pEnd; ++p) {
8268     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8269     PetscCall(DMPlexGetCone(dm, p, &cone));
8270     for (c = 0; c < coneSize; ++c) {
8271       PetscBool dup = PETSC_FALSE;
8272       PetscInt  d;
8273       for (d = c - 1; d >= 0; --d) {
8274         if (cone[c] == cone[d]) {
8275           dup = PETSC_TRUE;
8276           break;
8277         }
8278       }
8279       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8280       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8281       for (s = 0; s < supportSize; ++s) {
8282         if (support[s] == p) break;
8283       }
8284       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8285         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8286         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8287         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8288         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8289         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8290         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8291         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]);
8292         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8293       }
8294     }
8295     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8296     if (p != pp) {
8297       storagecheck = PETSC_FALSE;
8298       continue;
8299     }
8300     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8301     PetscCall(DMPlexGetSupport(dm, p, &support));
8302     for (s = 0; s < supportSize; ++s) {
8303       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8304       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8305       for (c = 0; c < coneSize; ++c) {
8306         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8307         if (cone[c] != pp) {
8308           c = 0;
8309           break;
8310         }
8311         if (cone[c] == p) break;
8312       }
8313       if (c >= coneSize) {
8314         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8315         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8316         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8317         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8318         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8319         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8320         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8321       }
8322     }
8323   }
8324   if (storagecheck) {
8325     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8326     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8327     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8328   }
8329   PetscFunctionReturn(0);
8330 }
8331 
8332 /*
8333   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.
8334 */
8335 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit) {
8336   DMPolytopeType  cct;
8337   PetscInt        ptpoints[4];
8338   const PetscInt *cone, *ccone, *ptcone;
8339   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8340 
8341   PetscFunctionBegin;
8342   *unsplit = 0;
8343   switch (ct) {
8344   case DM_POLYTOPE_POINT_PRISM_TENSOR: ptpoints[npt++] = c; break;
8345   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8346     PetscCall(DMPlexGetCone(dm, c, &cone));
8347     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8348     for (cp = 0; cp < coneSize; ++cp) {
8349       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8350       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8351     }
8352     break;
8353   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8354   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8355     PetscCall(DMPlexGetCone(dm, c, &cone));
8356     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8357     for (cp = 0; cp < coneSize; ++cp) {
8358       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8359       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8360       for (ccp = 0; ccp < cconeSize; ++ccp) {
8361         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8362         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8363           PetscInt p;
8364           for (p = 0; p < npt; ++p)
8365             if (ptpoints[p] == ccone[ccp]) break;
8366           if (p == npt) ptpoints[npt++] = ccone[ccp];
8367         }
8368       }
8369     }
8370     break;
8371   default: break;
8372   }
8373   for (pt = 0; pt < npt; ++pt) {
8374     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8375     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8376   }
8377   PetscFunctionReturn(0);
8378 }
8379 
8380 /*@
8381   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8382 
8383   Input Parameters:
8384 + dm - The DMPlex object
8385 - cellHeight - Normally 0
8386 
8387   Notes:
8388   This is a useful diagnostic when creating meshes programmatically.
8389   Currently applicable only to homogeneous simplex or tensor meshes.
8390 
8391   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8392 
8393   Level: developer
8394 
8395 .seealso: `DMCreate()`, `DMSetFromOptions()`
8396 @*/
8397 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight) {
8398   DMPlexInterpolatedFlag interp;
8399   DMPolytopeType         ct;
8400   PetscInt               vStart, vEnd, cStart, cEnd, c;
8401 
8402   PetscFunctionBegin;
8403   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8404   PetscCall(DMPlexIsInterpolated(dm, &interp));
8405   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8406   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8407   for (c = cStart; c < cEnd; ++c) {
8408     PetscInt *closure = NULL;
8409     PetscInt  coneSize, closureSize, cl, Nv = 0;
8410 
8411     PetscCall(DMPlexGetCellType(dm, c, &ct));
8412     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8413     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8414     if (interp == DMPLEX_INTERPOLATED_FULL) {
8415       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8416       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));
8417     }
8418     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8419     for (cl = 0; cl < closureSize * 2; cl += 2) {
8420       const PetscInt p = closure[cl];
8421       if ((p >= vStart) && (p < vEnd)) ++Nv;
8422     }
8423     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8424     /* Special Case: Tensor faces with identified vertices */
8425     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8426       PetscInt unsplit;
8427 
8428       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8429       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8430     }
8431     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));
8432   }
8433   PetscFunctionReturn(0);
8434 }
8435 
8436 /*@
8437   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8438 
8439   Collective
8440 
8441   Input Parameters:
8442 + dm - The DMPlex object
8443 - cellHeight - Normally 0
8444 
8445   Notes:
8446   This is a useful diagnostic when creating meshes programmatically.
8447   This routine is only relevant for meshes that are fully interpolated across all ranks.
8448   It will error out if a partially interpolated mesh is given on some rank.
8449   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8450 
8451   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8452 
8453   Level: developer
8454 
8455 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8456 @*/
8457 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight) {
8458   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8459   DMPlexInterpolatedFlag interpEnum;
8460 
8461   PetscFunctionBegin;
8462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8463   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8464   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8465   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8466     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8467     PetscFunctionReturn(0);
8468   }
8469 
8470   PetscCall(DMGetDimension(dm, &dim));
8471   PetscCall(DMPlexGetDepth(dm, &depth));
8472   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8473   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8474     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8475     for (c = cStart; c < cEnd; ++c) {
8476       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8477       const DMPolytopeType *faceTypes;
8478       DMPolytopeType        ct;
8479       PetscInt              numFaces, coneSize, f;
8480       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8481 
8482       PetscCall(DMPlexGetCellType(dm, c, &ct));
8483       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8484       if (unsplit) continue;
8485       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8486       PetscCall(DMPlexGetCone(dm, c, &cone));
8487       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8488       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8489       for (cl = 0; cl < closureSize * 2; cl += 2) {
8490         const PetscInt p = closure[cl];
8491         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8492       }
8493       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8494       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);
8495       for (f = 0; f < numFaces; ++f) {
8496         DMPolytopeType fct;
8497         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8498 
8499         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8500         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8501         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8502           const PetscInt p = fclosure[cl];
8503           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8504         }
8505         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]);
8506         for (v = 0; v < fnumCorners; ++v) {
8507           if (fclosure[v] != faces[fOff + v]) {
8508             PetscInt v1;
8509 
8510             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8511             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8512             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8513             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8514             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8515             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]);
8516           }
8517         }
8518         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8519         fOff += faceSizes[f];
8520       }
8521       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8522       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8523     }
8524   }
8525   PetscFunctionReturn(0);
8526 }
8527 
8528 /*@
8529   DMPlexCheckGeometry - Check the geometry of mesh cells
8530 
8531   Input Parameter:
8532 . dm - The DMPlex object
8533 
8534   Notes:
8535   This is a useful diagnostic when creating meshes programmatically.
8536 
8537   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8538 
8539   Level: developer
8540 
8541 .seealso: `DMCreate()`, `DMSetFromOptions()`
8542 @*/
8543 PetscErrorCode DMPlexCheckGeometry(DM dm) {
8544   Vec       coordinates;
8545   PetscReal detJ, J[9], refVol = 1.0;
8546   PetscReal vol;
8547   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8548 
8549   PetscFunctionBegin;
8550   PetscCall(DMGetDimension(dm, &dim));
8551   PetscCall(DMGetCoordinateDim(dm, &dE));
8552   if (dim != dE) PetscFunctionReturn(0);
8553   PetscCall(DMPlexGetDepth(dm, &depth));
8554   for (d = 0; d < dim; ++d) refVol *= 2.0;
8555   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8556   /* Make sure local coordinates are created, because that step is collective */
8557   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8558   for (c = cStart; c < cEnd; ++c) {
8559     DMPolytopeType ct;
8560     PetscInt       unsplit;
8561     PetscBool      ignoreZeroVol = PETSC_FALSE;
8562 
8563     PetscCall(DMPlexGetCellType(dm, c, &ct));
8564     switch (ct) {
8565     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8566     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8567     case DM_POLYTOPE_QUAD_PRISM_TENSOR: ignoreZeroVol = PETSC_TRUE; break;
8568     default: break;
8569     }
8570     switch (ct) {
8571     case DM_POLYTOPE_TRI_PRISM:
8572     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8573     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8574     case DM_POLYTOPE_PYRAMID: continue;
8575     default: break;
8576     }
8577     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8578     if (unsplit) continue;
8579     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8580     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);
8581     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8582     /* This should work with periodicity since DG coordinates should be used */
8583     if (depth > 1) {
8584       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8585       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);
8586       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8587     }
8588   }
8589   PetscFunctionReturn(0);
8590 }
8591 
8592 /*@
8593   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8594 
8595   Collective
8596 
8597   Input Parameters:
8598 + dm - The DMPlex object
8599 . pointSF - The Point SF, or NULL for Point SF attached to DM
8600 - allowExtraRoots - Flag to allow extra points not present in the DM
8601 
8602   Notes:
8603   This is mainly intended for debugging/testing purposes.
8604 
8605   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8606 
8607   Extra roots can come from priodic cuts, where additional points appear on the boundary
8608 
8609   Level: developer
8610 
8611 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8612 @*/
8613 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots) {
8614   PetscInt           l, nleaves, nroots, overlap;
8615   const PetscInt    *locals;
8616   const PetscSFNode *remotes;
8617   PetscBool          distributed;
8618   MPI_Comm           comm;
8619   PetscMPIInt        rank;
8620 
8621   PetscFunctionBegin;
8622   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8623   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8624   else pointSF = dm->sf;
8625   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8626   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8627   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8628   {
8629     PetscMPIInt mpiFlag;
8630 
8631     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8632     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8633   }
8634   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8635   PetscCall(DMPlexIsDistributed(dm, &distributed));
8636   if (!distributed) {
8637     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);
8638     PetscFunctionReturn(0);
8639   }
8640   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);
8641   PetscCall(DMPlexGetOverlap(dm, &overlap));
8642 
8643   /* Check SF graph is compatible with DMPlex chart */
8644   {
8645     PetscInt pStart, pEnd, maxLeaf;
8646 
8647     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8648     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8649     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8650     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8651   }
8652 
8653   /* Check Point SF has no local points referenced */
8654   for (l = 0; l < nleaves; l++) {
8655     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);
8656   }
8657 
8658   /* Check there are no cells in interface */
8659   if (!overlap) {
8660     PetscInt cellHeight, cStart, cEnd;
8661 
8662     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8663     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8664     for (l = 0; l < nleaves; ++l) {
8665       const PetscInt point = locals ? locals[l] : l;
8666 
8667       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8668     }
8669   }
8670 
8671   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8672   {
8673     const PetscInt *rootdegree;
8674 
8675     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8676     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8677     for (l = 0; l < nleaves; ++l) {
8678       const PetscInt  point = locals ? locals[l] : l;
8679       const PetscInt *cone;
8680       PetscInt        coneSize, c, idx;
8681 
8682       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8683       PetscCall(DMPlexGetCone(dm, point, &cone));
8684       for (c = 0; c < coneSize; ++c) {
8685         if (!rootdegree[cone[c]]) {
8686           if (locals) {
8687             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8688           } else {
8689             idx = (cone[c] < nleaves) ? cone[c] : -1;
8690           }
8691           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8692         }
8693       }
8694     }
8695   }
8696   PetscFunctionReturn(0);
8697 }
8698 
8699 /*@
8700   DMPlexCheck - Perform various checks of Plex sanity
8701 
8702   Input Parameter:
8703 . dm - The DMPlex object
8704 
8705   Notes:
8706   This is a useful diagnostic when creating meshes programmatically.
8707 
8708   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8709 
8710   Currently does not include DMPlexCheckCellShape().
8711 
8712   Level: developer
8713 
8714 .seealso: DMCreate(), DMSetFromOptions()
8715 @*/
8716 PetscErrorCode DMPlexCheck(DM dm) {
8717   PetscInt cellHeight;
8718 
8719   PetscFunctionBegin;
8720   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8721   PetscCall(DMPlexCheckSymmetry(dm));
8722   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8723   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8724   PetscCall(DMPlexCheckGeometry(dm));
8725   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
8726   PetscCall(DMPlexCheckInterfaceCones(dm));
8727   PetscFunctionReturn(0);
8728 }
8729 
8730 typedef struct cell_stats {
8731   PetscReal min, max, sum, squaresum;
8732   PetscInt  count;
8733 } cell_stats_t;
8734 
8735 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype) {
8736   PetscInt i, N = *len;
8737 
8738   for (i = 0; i < N; i++) {
8739     cell_stats_t *A = (cell_stats_t *)a;
8740     cell_stats_t *B = (cell_stats_t *)b;
8741 
8742     B->min = PetscMin(A->min, B->min);
8743     B->max = PetscMax(A->max, B->max);
8744     B->sum += A->sum;
8745     B->squaresum += A->squaresum;
8746     B->count += A->count;
8747   }
8748 }
8749 
8750 /*@
8751   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8752 
8753   Collective on dm
8754 
8755   Input Parameters:
8756 + dm        - The DMPlex object
8757 . output    - If true, statistics will be displayed on stdout
8758 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8759 
8760   Notes:
8761   This is mainly intended for debugging/testing purposes.
8762 
8763   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8764 
8765   Level: developer
8766 
8767 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8768 @*/
8769 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit) {
8770   DM           dmCoarse;
8771   cell_stats_t stats, globalStats;
8772   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
8773   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8774   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8775   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8776   PetscMPIInt  rank, size;
8777 
8778   PetscFunctionBegin;
8779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8780   stats.min = PETSC_MAX_REAL;
8781   stats.max = PETSC_MIN_REAL;
8782   stats.sum = stats.squaresum = 0.;
8783   stats.count                 = 0;
8784 
8785   PetscCallMPI(MPI_Comm_size(comm, &size));
8786   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8787   PetscCall(DMGetCoordinateDim(dm, &cdim));
8788   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8789   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
8790   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8791   for (c = cStart; c < cEnd; c++) {
8792     PetscInt  i;
8793     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8794 
8795     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
8796     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8797     for (i = 0; i < PetscSqr(cdim); ++i) {
8798       frobJ += J[i] * J[i];
8799       frobInvJ += invJ[i] * invJ[i];
8800     }
8801     cond2 = frobJ * frobInvJ;
8802     cond  = PetscSqrtReal(cond2);
8803 
8804     stats.min = PetscMin(stats.min, cond);
8805     stats.max = PetscMax(stats.max, cond);
8806     stats.sum += cond;
8807     stats.squaresum += cond2;
8808     stats.count++;
8809     if (output && cond > limit) {
8810       PetscSection coordSection;
8811       Vec          coordsLocal;
8812       PetscScalar *coords = NULL;
8813       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8814 
8815       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8816       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8817       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8818       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
8819       for (i = 0; i < Nv / cdim; ++i) {
8820         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8821         for (d = 0; d < cdim; ++d) {
8822           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8823           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
8824         }
8825         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8826       }
8827       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8828       for (cl = 0; cl < clSize * 2; cl += 2) {
8829         const PetscInt edge = closure[cl];
8830 
8831         if ((edge >= eStart) && (edge < eEnd)) {
8832           PetscReal len;
8833 
8834           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8835           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
8836         }
8837       }
8838       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8839       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8840     }
8841   }
8842   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8843 
8844   if (size > 1) {
8845     PetscMPIInt  blockLengths[2] = {4, 1};
8846     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
8847     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
8848     MPI_Op       statReduce;
8849 
8850     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
8851     PetscCallMPI(MPI_Type_commit(&statType));
8852     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8853     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
8854     PetscCallMPI(MPI_Op_free(&statReduce));
8855     PetscCallMPI(MPI_Type_free(&statType));
8856   } else {
8857     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
8858   }
8859   if (rank == 0) {
8860     count = globalStats.count;
8861     min   = globalStats.min;
8862     max   = globalStats.max;
8863     mean  = globalStats.sum / globalStats.count;
8864     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
8865   }
8866 
8867   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));
8868   PetscCall(PetscFree2(J, invJ));
8869 
8870   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
8871   if (dmCoarse) {
8872     PetscBool isplex;
8873 
8874     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
8875     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
8876   }
8877   PetscFunctionReturn(0);
8878 }
8879 
8880 /*@
8881   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8882   orthogonal quality below given tolerance.
8883 
8884   Collective on dm
8885 
8886   Input Parameters:
8887 + dm   - The DMPlex object
8888 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8889 - atol - [0, 1] Absolute tolerance for tagging cells.
8890 
8891   Output Parameters:
8892 + OrthQual      - Vec containing orthogonal quality per cell
8893 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8894 
8895   Options Database Keys:
8896 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8897 supported.
8898 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8899 
8900   Notes:
8901   Orthogonal quality is given by the following formula:
8902 
8903   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8904 
8905   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
8906   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8907   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8908   calculating the cosine of the angle between these vectors.
8909 
8910   Orthogonal quality ranges from 1 (best) to 0 (worst).
8911 
8912   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8913   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8914 
8915   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8916 
8917   Level: intermediate
8918 
8919 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8920 @*/
8921 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel) {
8922   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8923   PetscInt              *idx;
8924   PetscScalar           *oqVals;
8925   const PetscScalar     *cellGeomArr, *faceGeomArr;
8926   PetscReal             *ci, *fi, *Ai;
8927   MPI_Comm               comm;
8928   Vec                    cellgeom, facegeom;
8929   DM                     dmFace, dmCell;
8930   IS                     glob;
8931   ISLocalToGlobalMapping ltog;
8932   PetscViewer            vwr;
8933 
8934   PetscFunctionBegin;
8935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8936   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
8937   PetscValidPointer(OrthQual, 4);
8938   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
8939   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8940   PetscCall(DMGetDimension(dm, &nc));
8941   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8942   {
8943     DMPlexInterpolatedFlag interpFlag;
8944 
8945     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8946     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8947       PetscMPIInt rank;
8948 
8949       PetscCallMPI(MPI_Comm_rank(comm, &rank));
8950       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8951     }
8952   }
8953   if (OrthQualLabel) {
8954     PetscValidPointer(OrthQualLabel, 5);
8955     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
8956     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
8957   } else {
8958     *OrthQualLabel = NULL;
8959   }
8960   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8961   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8962   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
8963   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
8964   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
8965   PetscCall(VecCreate(comm, OrthQual));
8966   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
8967   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
8968   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
8969   PetscCall(VecSetUp(*OrthQual));
8970   PetscCall(ISDestroy(&glob));
8971   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
8972   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
8973   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
8974   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
8975   PetscCall(VecGetDM(cellgeom, &dmCell));
8976   PetscCall(VecGetDM(facegeom, &dmFace));
8977   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
8978   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
8979     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
8980     PetscInt         cellarr[2], *adj = NULL;
8981     PetscScalar     *cArr, *fArr;
8982     PetscReal        minvalc = 1.0, minvalf = 1.0;
8983     PetscFVCellGeom *cg;
8984 
8985     idx[cellIter] = cell - cStart;
8986     cellarr[0]    = cell;
8987     /* Make indexing into cellGeom easier */
8988     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
8989     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
8990     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
8991     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
8992     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
8993       PetscInt         i;
8994       const PetscInt   neigh  = adj[cellneigh];
8995       PetscReal        normci = 0, normfi = 0, normai = 0;
8996       PetscFVCellGeom *cgneigh;
8997       PetscFVFaceGeom *fg;
8998 
8999       /* Don't count ourselves in the neighbor list */
9000       if (neigh == cell) continue;
9001       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9002       cellarr[1] = neigh;
9003       {
9004         PetscInt        numcovpts;
9005         const PetscInt *covpts;
9006 
9007         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9008         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9009         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9010       }
9011 
9012       /* Compute c_i, f_i and their norms */
9013       for (i = 0; i < nc; i++) {
9014         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9015         fi[i] = fg->centroid[i] - cg->centroid[i];
9016         Ai[i] = fg->normal[i];
9017         normci += PetscPowReal(ci[i], 2);
9018         normfi += PetscPowReal(fi[i], 2);
9019         normai += PetscPowReal(Ai[i], 2);
9020       }
9021       normci = PetscSqrtReal(normci);
9022       normfi = PetscSqrtReal(normfi);
9023       normai = PetscSqrtReal(normai);
9024 
9025       /* Normalize and compute for each face-cell-normal pair */
9026       for (i = 0; i < nc; i++) {
9027         ci[i] = ci[i] / normci;
9028         fi[i] = fi[i] / normfi;
9029         Ai[i] = Ai[i] / normai;
9030         /* PetscAbs because I don't know if normals are guaranteed to point out */
9031         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9032         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9033       }
9034       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9035       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9036     }
9037     PetscCall(PetscFree(adj));
9038     PetscCall(PetscFree2(cArr, fArr));
9039     /* Defer to cell if they're equal */
9040     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9041     if (OrthQualLabel) {
9042       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9043     }
9044   }
9045   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9046   PetscCall(VecAssemblyBegin(*OrthQual));
9047   PetscCall(VecAssemblyEnd(*OrthQual));
9048   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9049   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9050   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9051   if (OrthQualLabel) {
9052     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9053   }
9054   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9055   PetscCall(PetscViewerDestroy(&vwr));
9056   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9057   PetscFunctionReturn(0);
9058 }
9059 
9060 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9061  * interpolator construction */
9062 static PetscErrorCode DMGetFullDM(DM dm, DM *odm) {
9063   PetscSection section, newSection, gsection;
9064   PetscSF      sf;
9065   PetscBool    hasConstraints, ghasConstraints;
9066 
9067   PetscFunctionBegin;
9068   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9069   PetscValidPointer(odm, 2);
9070   PetscCall(DMGetLocalSection(dm, &section));
9071   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9072   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9073   if (!ghasConstraints) {
9074     PetscCall(PetscObjectReference((PetscObject)dm));
9075     *odm = dm;
9076     PetscFunctionReturn(0);
9077   }
9078   PetscCall(DMClone(dm, odm));
9079   PetscCall(DMCopyFields(dm, *odm));
9080   PetscCall(DMGetLocalSection(*odm, &newSection));
9081   PetscCall(DMGetPointSF(*odm, &sf));
9082   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9083   PetscCall(DMSetGlobalSection(*odm, gsection));
9084   PetscCall(PetscSectionDestroy(&gsection));
9085   PetscFunctionReturn(0);
9086 }
9087 
9088 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift) {
9089   DM        dmco, dmfo;
9090   Mat       interpo;
9091   Vec       rscale;
9092   Vec       cglobalo, clocal;
9093   Vec       fglobal, fglobalo, flocal;
9094   PetscBool regular;
9095 
9096   PetscFunctionBegin;
9097   PetscCall(DMGetFullDM(dmc, &dmco));
9098   PetscCall(DMGetFullDM(dmf, &dmfo));
9099   PetscCall(DMSetCoarseDM(dmfo, dmco));
9100   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9101   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9102   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9103   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9104   PetscCall(DMCreateLocalVector(dmc, &clocal));
9105   PetscCall(VecSet(cglobalo, 0.));
9106   PetscCall(VecSet(clocal, 0.));
9107   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9108   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9109   PetscCall(DMCreateLocalVector(dmf, &flocal));
9110   PetscCall(VecSet(fglobal, 0.));
9111   PetscCall(VecSet(fglobalo, 0.));
9112   PetscCall(VecSet(flocal, 0.));
9113   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9114   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9115   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9116   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9117   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9118   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9119   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9120   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9121   *shift = fglobal;
9122   PetscCall(VecDestroy(&flocal));
9123   PetscCall(VecDestroy(&fglobalo));
9124   PetscCall(VecDestroy(&clocal));
9125   PetscCall(VecDestroy(&cglobalo));
9126   PetscCall(VecDestroy(&rscale));
9127   PetscCall(MatDestroy(&interpo));
9128   PetscCall(DMDestroy(&dmfo));
9129   PetscCall(DMDestroy(&dmco));
9130   PetscFunctionReturn(0);
9131 }
9132 
9133 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol) {
9134   PetscObject shifto;
9135   Vec         shift;
9136 
9137   PetscFunctionBegin;
9138   if (!interp) {
9139     Vec rscale;
9140 
9141     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9142     PetscCall(VecDestroy(&rscale));
9143   } else {
9144     PetscCall(PetscObjectReference((PetscObject)interp));
9145   }
9146   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9147   if (!shifto) {
9148     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9149     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9150     shifto = (PetscObject)shift;
9151     PetscCall(VecDestroy(&shift));
9152   }
9153   shift = (Vec)shifto;
9154   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9155   PetscCall(VecAXPY(fineSol, 1.0, shift));
9156   PetscCall(MatDestroy(&interp));
9157   PetscFunctionReturn(0);
9158 }
9159 
9160 /* Pointwise interpolation
9161      Just code FEM for now
9162      u^f = I u^c
9163      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9164      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9165      I_{ij} = psi^f_i phi^c_j
9166 */
9167 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling) {
9168   PetscSection gsc, gsf;
9169   PetscInt     m, n;
9170   void        *ctx;
9171   DM           cdm;
9172   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9173 
9174   PetscFunctionBegin;
9175   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9176   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9177   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9178   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9179 
9180   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9181   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9182   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9183   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9184   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9185 
9186   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9187   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9188   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9189   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9190   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9191   if (scaling) {
9192     /* Use naive scaling */
9193     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9194   }
9195   PetscFunctionReturn(0);
9196 }
9197 
9198 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat) {
9199   VecScatter ctx;
9200 
9201   PetscFunctionBegin;
9202   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9203   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9204   PetscCall(VecScatterDestroy(&ctx));
9205   PetscFunctionReturn(0);
9206 }
9207 
9208 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[]) {
9209   const PetscInt Nc = uOff[1] - uOff[0];
9210   PetscInt       c;
9211   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9212 }
9213 
9214 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass) {
9215   DM           dmc;
9216   PetscDS      ds;
9217   Vec          ones, locmass;
9218   IS           cellIS;
9219   PetscFormKey key;
9220   PetscInt     depth;
9221 
9222   PetscFunctionBegin;
9223   PetscCall(DMClone(dm, &dmc));
9224   PetscCall(DMCopyDisc(dm, dmc));
9225   PetscCall(DMGetDS(dmc, &ds));
9226   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9227   PetscCall(DMCreateGlobalVector(dmc, mass));
9228   PetscCall(DMGetLocalVector(dmc, &ones));
9229   PetscCall(DMGetLocalVector(dmc, &locmass));
9230   PetscCall(DMPlexGetDepth(dmc, &depth));
9231   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9232   PetscCall(VecSet(locmass, 0.0));
9233   PetscCall(VecSet(ones, 1.0));
9234   key.label = NULL;
9235   key.value = 0;
9236   key.field = 0;
9237   key.part  = 0;
9238   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9239   PetscCall(ISDestroy(&cellIS));
9240   PetscCall(VecSet(*mass, 0.0));
9241   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9242   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9243   PetscCall(DMRestoreLocalVector(dmc, &ones));
9244   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9245   PetscCall(DMDestroy(&dmc));
9246   PetscFunctionReturn(0);
9247 }
9248 
9249 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass) {
9250   PetscSection gsc, gsf;
9251   PetscInt     m, n;
9252   void        *ctx;
9253   DM           cdm;
9254   PetscBool    regular;
9255 
9256   PetscFunctionBegin;
9257   if (dmFine == dmCoarse) {
9258     DM            dmc;
9259     PetscDS       ds;
9260     PetscWeakForm wf;
9261     Vec           u;
9262     IS            cellIS;
9263     PetscFormKey  key;
9264     PetscInt      depth;
9265 
9266     PetscCall(DMClone(dmFine, &dmc));
9267     PetscCall(DMCopyDisc(dmFine, dmc));
9268     PetscCall(DMGetDS(dmc, &ds));
9269     PetscCall(PetscDSGetWeakForm(ds, &wf));
9270     PetscCall(PetscWeakFormClear(wf));
9271     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9272     PetscCall(DMCreateMatrix(dmc, mass));
9273     PetscCall(DMGetLocalVector(dmc, &u));
9274     PetscCall(DMPlexGetDepth(dmc, &depth));
9275     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9276     PetscCall(MatZeroEntries(*mass));
9277     key.label = NULL;
9278     key.value = 0;
9279     key.field = 0;
9280     key.part  = 0;
9281     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9282     PetscCall(ISDestroy(&cellIS));
9283     PetscCall(DMRestoreLocalVector(dmc, &u));
9284     PetscCall(DMDestroy(&dmc));
9285   } else {
9286     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9287     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9288     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9289     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9290 
9291     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9292     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9293     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9294     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9295 
9296     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9297     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9298     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9299     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9300   }
9301   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9302   PetscFunctionReturn(0);
9303 }
9304 
9305 /*@
9306   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9307 
9308   Input Parameter:
9309 . dm - The DMPlex object
9310 
9311   Output Parameter:
9312 . regular - The flag
9313 
9314   Level: intermediate
9315 
9316 .seealso: `DMPlexSetRegularRefinement()`
9317 @*/
9318 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular) {
9319   PetscFunctionBegin;
9320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9321   PetscValidBoolPointer(regular, 2);
9322   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9323   PetscFunctionReturn(0);
9324 }
9325 
9326 /*@
9327   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9328 
9329   Input Parameters:
9330 + dm - The DMPlex object
9331 - regular - The flag
9332 
9333   Level: intermediate
9334 
9335 .seealso: `DMPlexGetRegularRefinement()`
9336 @*/
9337 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular) {
9338   PetscFunctionBegin;
9339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9340   ((DM_Plex *)dm->data)->regularRefinement = regular;
9341   PetscFunctionReturn(0);
9342 }
9343 
9344 /* anchors */
9345 /*@
9346   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9347   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9348 
9349   not collective
9350 
9351   Input Parameter:
9352 . dm - The DMPlex object
9353 
9354   Output Parameters:
9355 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9356 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9357 
9358   Level: intermediate
9359 
9360 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9361 @*/
9362 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS) {
9363   DM_Plex *plex = (DM_Plex *)dm->data;
9364 
9365   PetscFunctionBegin;
9366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9367   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9368   if (anchorSection) *anchorSection = plex->anchorSection;
9369   if (anchorIS) *anchorIS = plex->anchorIS;
9370   PetscFunctionReturn(0);
9371 }
9372 
9373 /*@
9374   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9375   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9376   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9377 
9378   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9379   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9380 
9381   collective on dm
9382 
9383   Input Parameters:
9384 + dm - The DMPlex object
9385 . 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).
9386 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9387 
9388   The reference counts of anchorSection and anchorIS are incremented.
9389 
9390   Level: intermediate
9391 
9392 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9393 @*/
9394 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS) {
9395   DM_Plex    *plex = (DM_Plex *)dm->data;
9396   PetscMPIInt result;
9397 
9398   PetscFunctionBegin;
9399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9400   if (anchorSection) {
9401     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9402     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9403     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9404   }
9405   if (anchorIS) {
9406     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9407     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9408     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9409   }
9410 
9411   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9412   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9413   plex->anchorSection = anchorSection;
9414 
9415   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9416   PetscCall(ISDestroy(&plex->anchorIS));
9417   plex->anchorIS = anchorIS;
9418 
9419   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9420     PetscInt        size, a, pStart, pEnd;
9421     const PetscInt *anchors;
9422 
9423     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9424     PetscCall(ISGetLocalSize(anchorIS, &size));
9425     PetscCall(ISGetIndices(anchorIS, &anchors));
9426     for (a = 0; a < size; a++) {
9427       PetscInt p;
9428 
9429       p = anchors[a];
9430       if (p >= pStart && p < pEnd) {
9431         PetscInt dof;
9432 
9433         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9434         if (dof) {
9435           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9436           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9437         }
9438       }
9439     }
9440     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9441   }
9442   /* reset the generic constraints */
9443   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9444   PetscFunctionReturn(0);
9445 }
9446 
9447 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec) {
9448   PetscSection anchorSection;
9449   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9450 
9451   PetscFunctionBegin;
9452   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9453   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9454   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9455   PetscCall(PetscSectionGetNumFields(section, &numFields));
9456   if (numFields) {
9457     PetscInt f;
9458     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9459 
9460     for (f = 0; f < numFields; f++) {
9461       PetscInt numComp;
9462 
9463       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9464       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9465     }
9466   }
9467   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9468   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9469   pStart = PetscMax(pStart, sStart);
9470   pEnd   = PetscMin(pEnd, sEnd);
9471   pEnd   = PetscMax(pStart, pEnd);
9472   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9473   for (p = pStart; p < pEnd; p++) {
9474     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9475     if (dof) {
9476       PetscCall(PetscSectionGetDof(section, p, &dof));
9477       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9478       for (f = 0; f < numFields; f++) {
9479         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9480         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9481       }
9482     }
9483   }
9484   PetscCall(PetscSectionSetUp(*cSec));
9485   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9486   PetscFunctionReturn(0);
9487 }
9488 
9489 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat) {
9490   PetscSection    aSec;
9491   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9492   const PetscInt *anchors;
9493   PetscInt        numFields, f;
9494   IS              aIS;
9495   MatType         mtype;
9496   PetscBool       iscuda, iskokkos;
9497 
9498   PetscFunctionBegin;
9499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9500   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9501   PetscCall(PetscSectionGetStorageSize(section, &n));
9502   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9503   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9504   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9505   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9506   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9507   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9508   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9509   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9510   else mtype = MATSEQAIJ;
9511   PetscCall(MatSetType(*cMat, mtype));
9512   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9513   PetscCall(ISGetIndices(aIS, &anchors));
9514   /* cSec will be a subset of aSec and section */
9515   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9516   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9517   PetscCall(PetscMalloc1(m + 1, &i));
9518   i[0] = 0;
9519   PetscCall(PetscSectionGetNumFields(section, &numFields));
9520   for (p = pStart; p < pEnd; p++) {
9521     PetscInt rDof, rOff, r;
9522 
9523     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9524     if (!rDof) continue;
9525     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9526     if (numFields) {
9527       for (f = 0; f < numFields; f++) {
9528         annz = 0;
9529         for (r = 0; r < rDof; r++) {
9530           a = anchors[rOff + r];
9531           if (a < sStart || a >= sEnd) continue;
9532           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9533           annz += aDof;
9534         }
9535         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9536         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9537         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9538       }
9539     } else {
9540       annz = 0;
9541       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9542       for (q = 0; q < dof; q++) {
9543         a = anchors[rOff + q];
9544         if (a < sStart || a >= sEnd) continue;
9545         PetscCall(PetscSectionGetDof(section, a, &aDof));
9546         annz += aDof;
9547       }
9548       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9549       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9550       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9551     }
9552   }
9553   nnz = i[m];
9554   PetscCall(PetscMalloc1(nnz, &j));
9555   offset = 0;
9556   for (p = pStart; p < pEnd; p++) {
9557     if (numFields) {
9558       for (f = 0; f < numFields; f++) {
9559         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &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(PetscSectionGetFieldDof(section, a, f, &aDof));
9570             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9571             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9572           }
9573         }
9574       }
9575     } else {
9576       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9577       for (q = 0; q < dof; q++) {
9578         PetscInt rDof, rOff, r;
9579         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9580         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9581         for (r = 0; r < rDof; r++) {
9582           PetscInt s;
9583 
9584           a = anchors[rOff + r];
9585           if (a < sStart || a >= sEnd) continue;
9586           PetscCall(PetscSectionGetDof(section, a, &aDof));
9587           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9588           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9589         }
9590       }
9591     }
9592   }
9593   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9594   PetscCall(PetscFree(i));
9595   PetscCall(PetscFree(j));
9596   PetscCall(ISRestoreIndices(aIS, &anchors));
9597   PetscFunctionReturn(0);
9598 }
9599 
9600 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm) {
9601   DM_Plex     *plex = (DM_Plex *)dm->data;
9602   PetscSection anchorSection, section, cSec;
9603   Mat          cMat;
9604 
9605   PetscFunctionBegin;
9606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9607   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9608   if (anchorSection) {
9609     PetscInt Nf;
9610 
9611     PetscCall(DMGetLocalSection(dm, &section));
9612     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9613     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9614     PetscCall(DMGetNumFields(dm, &Nf));
9615     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9616     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9617     PetscCall(PetscSectionDestroy(&cSec));
9618     PetscCall(MatDestroy(&cMat));
9619   }
9620   PetscFunctionReturn(0);
9621 }
9622 
9623 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm) {
9624   IS           subis;
9625   PetscSection section, subsection;
9626 
9627   PetscFunctionBegin;
9628   PetscCall(DMGetLocalSection(dm, &section));
9629   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9630   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9631   /* Create subdomain */
9632   PetscCall(DMPlexFilter(dm, label, value, subdm));
9633   /* Create submodel */
9634   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9635   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9636   PetscCall(DMSetLocalSection(*subdm, subsection));
9637   PetscCall(PetscSectionDestroy(&subsection));
9638   PetscCall(DMCopyDisc(dm, *subdm));
9639   /* Create map from submodel to global model */
9640   if (is) {
9641     PetscSection    sectionGlobal, subsectionGlobal;
9642     IS              spIS;
9643     const PetscInt *spmap;
9644     PetscInt       *subIndices;
9645     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9646     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9647 
9648     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9649     PetscCall(ISGetIndices(spIS, &spmap));
9650     PetscCall(PetscSectionGetNumFields(section, &Nf));
9651     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9652     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9653     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9654     for (p = pStart; p < pEnd; ++p) {
9655       PetscInt gdof, pSubSize = 0;
9656 
9657       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9658       if (gdof > 0) {
9659         for (f = 0; f < Nf; ++f) {
9660           PetscInt fdof, fcdof;
9661 
9662           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9663           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9664           pSubSize += fdof - fcdof;
9665         }
9666         subSize += pSubSize;
9667         if (pSubSize) {
9668           if (bs < 0) {
9669             bs = pSubSize;
9670           } else if (bs != pSubSize) {
9671             /* Layout does not admit a pointwise block size */
9672             bs = 1;
9673           }
9674         }
9675       }
9676     }
9677     /* Must have same blocksize on all procs (some might have no points) */
9678     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
9679     bsLocal[1] = bs;
9680     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
9681     if (bsMinMax[0] != bsMinMax[1]) {
9682       bs = 1;
9683     } else {
9684       bs = bsMinMax[0];
9685     }
9686     PetscCall(PetscMalloc1(subSize, &subIndices));
9687     for (p = pStart; p < pEnd; ++p) {
9688       PetscInt gdof, goff;
9689 
9690       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9691       if (gdof > 0) {
9692         const PetscInt point = spmap[p];
9693 
9694         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9695         for (f = 0; f < Nf; ++f) {
9696           PetscInt fdof, fcdof, fc, f2, poff = 0;
9697 
9698           /* Can get rid of this loop by storing field information in the global section */
9699           for (f2 = 0; f2 < f; ++f2) {
9700             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9701             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9702             poff += fdof - fcdof;
9703           }
9704           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9705           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9706           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
9707         }
9708       }
9709     }
9710     PetscCall(ISRestoreIndices(spIS, &spmap));
9711     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9712     if (bs > 1) {
9713       /* We need to check that the block size does not come from non-contiguous fields */
9714       PetscInt i, j, set = 1;
9715       for (i = 0; i < subSize; i += bs) {
9716         for (j = 0; j < bs; ++j) {
9717           if (subIndices[i + j] != subIndices[i] + j) {
9718             set = 0;
9719             break;
9720           }
9721         }
9722       }
9723       if (set) PetscCall(ISSetBlockSize(*is, bs));
9724     }
9725     /* Attach nullspace */
9726     for (f = 0; f < Nf; ++f) {
9727       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9728       if ((*subdm)->nullspaceConstructors[f]) break;
9729     }
9730     if (f < Nf) {
9731       MatNullSpace nullSpace;
9732       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9733 
9734       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
9735       PetscCall(MatNullSpaceDestroy(&nullSpace));
9736     }
9737   }
9738   PetscFunctionReturn(0);
9739 }
9740 
9741 /*@
9742   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9743 
9744   Input Parameter:
9745 - dm - The DM
9746 
9747   Level: developer
9748 
9749   Options Database Keys:
9750 . -dm_plex_monitor_throughput - Activate the monitor
9751 
9752 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9753 @*/
9754 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy) {
9755 #if defined(PETSC_USE_LOG)
9756   PetscStageLog      stageLog;
9757   PetscLogEvent      event;
9758   PetscLogStage      stage;
9759   PetscEventPerfInfo eventInfo;
9760   PetscReal          cellRate, flopRate;
9761   PetscInt           cStart, cEnd, Nf, N;
9762   const char        *name;
9763 #endif
9764 
9765   PetscFunctionBegin;
9766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9767 #if defined(PETSC_USE_LOG)
9768   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
9769   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9770   PetscCall(DMGetNumFields(dm, &Nf));
9771   PetscCall(PetscLogGetStageLog(&stageLog));
9772   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9773   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9774   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9775   N        = (cEnd - cStart) * Nf * eventInfo.count;
9776   flopRate = eventInfo.flops / eventInfo.time;
9777   cellRate = N / eventInfo.time;
9778   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)));
9779 #else
9780   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9781 #endif
9782   PetscFunctionReturn(0);
9783 }
9784