xref: /petsc/src/dm/impls/plex/plex.c (revision 1015a2a477dd2695c1e05348307aec5fc5bfe5fb)
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 {
36   DMPolytopeType ct;
37   PetscInt       cStart, cEnd;
38 
39   PetscFunctionBegin;
40   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
41   if (cEnd <= cStart) {
42     *simplex = PETSC_FALSE;
43     PetscFunctionReturn(0);
44   }
45   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
46   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
47   PetscFunctionReturn(0);
48 }
49 
50 /*@
51   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
52 
53   Input Parameters:
54 + dm     - The DMPlex object
55 - height - The cell height in the Plex, 0 is the default
56 
57   Output Parameters:
58 + cStart - The first "normal" cell
59 - cEnd   - The upper bound on "normal"" cells
60 
61   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
62 
63   Level: developer
64 
65 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
66 @*/
67 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
68 {
69   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
70   PetscInt       cS, cE, c;
71 
72   PetscFunctionBegin;
73   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
74   for (c = cS; c < cE; ++c) {
75     DMPolytopeType cct;
76 
77     PetscCall(DMPlexGetCellType(dm, c, &cct));
78     if ((PetscInt)cct < 0) break;
79     switch (cct) {
80     case DM_POLYTOPE_POINT:
81     case DM_POLYTOPE_SEGMENT:
82     case DM_POLYTOPE_TRIANGLE:
83     case DM_POLYTOPE_QUADRILATERAL:
84     case DM_POLYTOPE_TETRAHEDRON:
85     case DM_POLYTOPE_HEXAHEDRON:
86       ct = cct;
87       break;
88     default:
89       break;
90     }
91     if (ct != DM_POLYTOPE_UNKNOWN) break;
92   }
93   if (ct != DM_POLYTOPE_UNKNOWN) {
94     DMLabel ctLabel;
95 
96     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
97     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
98     // Reset label for fast lookup
99     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
100   }
101   if (cStart) *cStart = cS;
102   if (cEnd) *cEnd = cE;
103   PetscFunctionReturn(0);
104 }
105 
106 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
107 {
108   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
109   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
110 
111   PetscFunctionBegin;
112   *ft = PETSC_VTK_INVALID;
113   PetscCall(DMGetCoordinateDim(dm, &cdim));
114   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
115   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
116   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
117   if (field >= 0) {
118     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
119     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
120   } else {
121     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
122     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
123   }
124   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
125   if (globalvcdof[0]) {
126     *sStart = vStart;
127     *sEnd   = vEnd;
128     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
129     else *ft = PETSC_VTK_POINT_FIELD;
130   } else if (globalvcdof[1]) {
131     *sStart = cStart;
132     *sEnd   = cEnd;
133     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
134     else *ft = PETSC_VTK_CELL_FIELD;
135   } else {
136     if (field >= 0) {
137       const char *fieldname;
138 
139       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
140       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
141     } else {
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
143     }
144   }
145   PetscFunctionReturn(0);
146 }
147 
148 /*@
149   DMPlexVecView1D - Plot many 1D solutions on the same line graph
150 
151   Collective on dm
152 
153   Input Parameters:
154 + dm - The DMPlex
155 . n  - The number of vectors
156 . u  - The array of local vectors
157 - viewer - The Draw viewer
158 
159   Level: advanced
160 
161 .seealso: `VecViewFromOptions()`, `VecView()`
162 @*/
163 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
164 {
165   PetscDS            ds;
166   PetscDraw          draw = NULL;
167   PetscDrawLG        lg;
168   Vec                coordinates;
169   const PetscScalar *coords, **sol;
170   PetscReal         *vals;
171   PetscInt          *Nc;
172   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
173   char             **names;
174 
175   PetscFunctionBegin;
176   PetscCall(DMGetDS(dm, &ds));
177   PetscCall(PetscDSGetNumFields(ds, &Nf));
178   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
179   PetscCall(PetscDSGetComponents(ds, &Nc));
180 
181   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
182   if (!draw) PetscFunctionReturn(0);
183   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
184 
185   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
186   for (i = 0, l = 0; i < n; ++i) {
187     const char *vname;
188 
189     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
190     for (f = 0; f < Nf; ++f) {
191       PetscObject disc;
192       const char *fname;
193       char        tmpname[PETSC_MAX_PATH_LEN];
194 
195       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
196       /* TODO Create names for components */
197       for (c = 0; c < Nc[f]; ++c, ++l) {
198         PetscCall(PetscObjectGetName(disc, &fname));
199         PetscCall(PetscStrcpy(tmpname, vname));
200         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
201         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
202         PetscCall(PetscStrallocpy(tmpname, &names[l]));
203       }
204     }
205   }
206   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
207   /* Just add P_1 support for now */
208   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
209   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
210   PetscCall(VecGetArrayRead(coordinates, &coords));
211   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
212   for (v = vStart; v < vEnd; ++v) {
213     PetscScalar *x, *svals;
214 
215     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
216     for (i = 0; i < n; ++i) {
217       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
218       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
219     }
220     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
221   }
222   PetscCall(VecRestoreArrayRead(coordinates, &coords));
223   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
224   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
225   PetscCall(PetscFree3(sol, names, vals));
226 
227   PetscCall(PetscDrawLGDraw(lg));
228   PetscCall(PetscDrawLGDestroy(&lg));
229   PetscFunctionReturn(0);
230 }
231 
232 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
233 {
234   DM dm;
235 
236   PetscFunctionBegin;
237   PetscCall(VecGetDM(u, &dm));
238   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
239   PetscFunctionReturn(0);
240 }
241 
242 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
243 {
244   DM                 dm;
245   PetscSection       s;
246   PetscDraw          draw, popup;
247   DM                 cdm;
248   PetscSection       coordSection;
249   Vec                coordinates;
250   const PetscScalar *coords, *array;
251   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
252   PetscReal          vbound[2], time;
253   PetscBool          flg;
254   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
255   const char        *name;
256   char               title[PETSC_MAX_PATH_LEN];
257 
258   PetscFunctionBegin;
259   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
260   PetscCall(VecGetDM(v, &dm));
261   PetscCall(DMGetCoordinateDim(dm, &dim));
262   PetscCall(DMGetLocalSection(dm, &s));
263   PetscCall(PetscSectionGetNumFields(s, &Nf));
264   PetscCall(DMGetCoarsenLevel(dm, &level));
265   PetscCall(DMGetCoordinateDM(dm, &cdm));
266   PetscCall(DMGetLocalSection(cdm, &coordSection));
267   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
268   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
269   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
270 
271   PetscCall(PetscObjectGetName((PetscObject)v, &name));
272   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
273 
274   PetscCall(VecGetLocalSize(coordinates, &N));
275   PetscCall(VecGetArrayRead(coordinates, &coords));
276   for (c = 0; c < N; c += dim) {
277     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
278     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
279     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
280     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
281   }
282   PetscCall(VecRestoreArrayRead(coordinates, &coords));
283   PetscCall(PetscDrawClear(draw));
284 
285   /* Could implement something like DMDASelectFields() */
286   for (f = 0; f < Nf; ++f) {
287     DM          fdm = dm;
288     Vec         fv  = v;
289     IS          fis;
290     char        prefix[PETSC_MAX_PATH_LEN];
291     const char *fname;
292 
293     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
294     PetscCall(PetscSectionGetFieldName(s, f, &fname));
295 
296     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
297     else prefix[0] = '\0';
298     if (Nf > 1) {
299       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
300       PetscCall(VecGetSubVector(v, fis, &fv));
301       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
302       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
303     }
304     for (comp = 0; comp < Nc; ++comp, ++w) {
305       PetscInt nmax = 2;
306 
307       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
308       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
309       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
310       PetscCall(PetscDrawSetTitle(draw, title));
311 
312       /* TODO Get max and min only for this component */
313       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
314       if (!flg) {
315         PetscCall(VecMin(fv, NULL, &vbound[0]));
316         PetscCall(VecMax(fv, NULL, &vbound[1]));
317         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
318       }
319       PetscCall(PetscDrawGetPopup(draw, &popup));
320       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
321       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
322 
323       PetscCall(VecGetArrayRead(fv, &array));
324       for (c = cStart; c < cEnd; ++c) {
325         PetscScalar *coords = NULL, *a   = NULL;
326         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
327 
328         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
329         if (a) {
330           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
331           color[1] = color[2] = color[3] = color[0];
332         } else {
333           PetscScalar *vals = NULL;
334           PetscInt     numVals, va;
335 
336           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
337           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);
338           switch (numVals / Nc) {
339           case 3: /* P1 Triangle */
340           case 4: /* P1 Quadrangle */
341             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
342             break;
343           case 6: /* P2 Triangle */
344           case 8: /* P2 Quadrangle */
345             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
346             break;
347           default:
348             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
349           }
350           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
351         }
352         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
353         switch (numCoords) {
354         case 6:
355         case 12: /* Localized triangle */
356           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]));
357           break;
358         case 8:
359         case 16: /* Localized quadrilateral */
360           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]));
361           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]));
362           break;
363         default:
364           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
365         }
366         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
367       }
368       PetscCall(VecRestoreArrayRead(fv, &array));
369       PetscCall(PetscDrawFlush(draw));
370       PetscCall(PetscDrawPause(draw));
371       PetscCall(PetscDrawSave(draw));
372     }
373     if (Nf > 1) {
374       PetscCall(VecRestoreSubVector(v, fis, &fv));
375       PetscCall(ISDestroy(&fis));
376       PetscCall(DMDestroy(&fdm));
377     }
378   }
379   PetscFunctionReturn(0);
380 }
381 
382 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
383 {
384   DM        dm;
385   PetscDraw draw;
386   PetscInt  dim;
387   PetscBool isnull;
388 
389   PetscFunctionBegin;
390   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
391   PetscCall(PetscDrawIsNull(draw, &isnull));
392   if (isnull) PetscFunctionReturn(0);
393 
394   PetscCall(VecGetDM(v, &dm));
395   PetscCall(DMGetCoordinateDim(dm, &dim));
396   switch (dim) {
397   case 1:
398     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
399     break;
400   case 2:
401     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
402     break;
403   default:
404     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
405   }
406   PetscFunctionReturn(0);
407 }
408 
409 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
410 {
411   DM                      dm;
412   Vec                     locv;
413   const char             *name;
414   PetscSection            section;
415   PetscInt                pStart, pEnd;
416   PetscInt                numFields;
417   PetscViewerVTKFieldType ft;
418 
419   PetscFunctionBegin;
420   PetscCall(VecGetDM(v, &dm));
421   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
422   PetscCall(PetscObjectGetName((PetscObject)v, &name));
423   PetscCall(PetscObjectSetName((PetscObject)locv, name));
424   PetscCall(VecCopy(v, locv));
425   PetscCall(DMGetLocalSection(dm, &section));
426   PetscCall(PetscSectionGetNumFields(section, &numFields));
427   if (!numFields) {
428     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
429     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
430   } else {
431     PetscInt f;
432 
433     for (f = 0; f < numFields; f++) {
434       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
435       if (ft == PETSC_VTK_INVALID) continue;
436       PetscCall(PetscObjectReference((PetscObject)locv));
437       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
438     }
439     PetscCall(VecDestroy(&locv));
440   }
441   PetscFunctionReturn(0);
442 }
443 
444 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
445 {
446   DM        dm;
447   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
448 
449   PetscFunctionBegin;
450   PetscCall(VecGetDM(v, &dm));
451   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
457   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
458     PetscInt    i, numFields;
459     PetscObject fe;
460     PetscBool   fem  = PETSC_FALSE;
461     Vec         locv = v;
462     const char *name;
463     PetscInt    step;
464     PetscReal   time;
465 
466     PetscCall(DMGetNumFields(dm, &numFields));
467     for (i = 0; i < numFields; i++) {
468       PetscCall(DMGetField(dm, i, NULL, &fe));
469       if (fe->classid == PETSCFE_CLASSID) {
470         fem = PETSC_TRUE;
471         break;
472       }
473     }
474     if (fem) {
475       PetscObject isZero;
476 
477       PetscCall(DMGetLocalVector(dm, &locv));
478       PetscCall(PetscObjectGetName((PetscObject)v, &name));
479       PetscCall(PetscObjectSetName((PetscObject)locv, name));
480       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
481       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
482       PetscCall(VecCopy(v, locv));
483       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
484       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
485     }
486     if (isvtk) {
487       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
488     } else if (ishdf5) {
489 #if defined(PETSC_HAVE_HDF5)
490       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
491 #else
492       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
493 #endif
494     } else if (isdraw) {
495       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
496     } else if (isglvis) {
497       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
498       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
499       PetscCall(VecView_GLVis(locv, viewer));
500     } else if (iscgns) {
501 #if defined(PETSC_HAVE_CGNS)
502       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
503 #else
504       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
505 #endif
506     }
507     if (fem) {
508       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
509       PetscCall(DMRestoreLocalVector(dm, &locv));
510     }
511   } else {
512     PetscBool isseq;
513 
514     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
515     if (isseq) PetscCall(VecView_Seq(v, viewer));
516     else PetscCall(VecView_MPI(v, viewer));
517   }
518   PetscFunctionReturn(0);
519 }
520 
521 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
522 {
523   DM        dm;
524   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
525 
526   PetscFunctionBegin;
527   PetscCall(VecGetDM(v, &dm));
528   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
535   if (isvtk || isdraw || isglvis || iscgns) {
536     Vec         locv;
537     PetscObject isZero;
538     const char *name;
539 
540     PetscCall(DMGetLocalVector(dm, &locv));
541     PetscCall(PetscObjectGetName((PetscObject)v, &name));
542     PetscCall(PetscObjectSetName((PetscObject)locv, name));
543     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
544     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
545     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
546     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
547     PetscCall(VecView_Plex_Local(locv, viewer));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
549     PetscCall(DMRestoreLocalVector(dm, &locv));
550   } else if (ishdf5) {
551 #if defined(PETSC_HAVE_HDF5)
552     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
553 #else
554     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
555 #endif
556   } else if (isexodusii) {
557 #if defined(PETSC_HAVE_EXODUSII)
558     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
559 #else
560     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
561 #endif
562   } else {
563     PetscBool isseq;
564 
565     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
566     if (isseq) PetscCall(VecView_Seq(v, viewer));
567     else PetscCall(VecView_MPI(v, viewer));
568   }
569   PetscFunctionReturn(0);
570 }
571 
572 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
573 {
574   DM                dm;
575   MPI_Comm          comm;
576   PetscViewerFormat format;
577   Vec               v;
578   PetscBool         isvtk, ishdf5;
579 
580   PetscFunctionBegin;
581   PetscCall(VecGetDM(originalv, &dm));
582   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
583   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
584   PetscCall(PetscViewerGetFormat(viewer, &format));
585   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
586   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
587   if (format == PETSC_VIEWER_NATIVE) {
588     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
589     /* this need a better fix */
590     if (dm->useNatural) {
591       if (dm->sfNatural) {
592         const char *vecname;
593         PetscInt    n, nroots;
594 
595         PetscCall(VecGetLocalSize(originalv, &n));
596         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
597         if (n == nroots) {
598           PetscCall(DMGetGlobalVector(dm, &v));
599           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
600           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
601           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
602           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
603         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
604       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
605     } else v = originalv;
606   } else v = originalv;
607 
608   if (ishdf5) {
609 #if defined(PETSC_HAVE_HDF5)
610     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
611 #else
612     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
613 #endif
614   } else if (isvtk) {
615     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
616   } else {
617     PetscBool isseq;
618 
619     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
620     if (isseq) PetscCall(VecView_Seq(v, viewer));
621     else PetscCall(VecView_MPI(v, viewer));
622   }
623   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
624   PetscFunctionReturn(0);
625 }
626 
627 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
628 {
629   DM        dm;
630   PetscBool ishdf5;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
636   if (ishdf5) {
637     DM          dmBC;
638     Vec         gv;
639     const char *name;
640 
641     PetscCall(DMGetOutputDM(dm, &dmBC));
642     PetscCall(DMGetGlobalVector(dmBC, &gv));
643     PetscCall(PetscObjectGetName((PetscObject)v, &name));
644     PetscCall(PetscObjectSetName((PetscObject)gv, name));
645     PetscCall(VecLoad_Default(gv, viewer));
646     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
647     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
648     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
649   } else PetscCall(VecLoad_Default(v, viewer));
650   PetscFunctionReturn(0);
651 }
652 
653 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
654 {
655   DM        dm;
656   PetscBool ishdf5, isexodusii;
657 
658   PetscFunctionBegin;
659   PetscCall(VecGetDM(v, &dm));
660   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
661   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
662   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
663   if (ishdf5) {
664 #if defined(PETSC_HAVE_HDF5)
665     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
666 #else
667     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
668 #endif
669   } else if (isexodusii) {
670 #if defined(PETSC_HAVE_EXODUSII)
671     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
672 #else
673     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
674 #endif
675   } else PetscCall(VecLoad_Default(v, viewer));
676   PetscFunctionReturn(0);
677 }
678 
679 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
680 {
681   DM                dm;
682   PetscViewerFormat format;
683   PetscBool         ishdf5;
684 
685   PetscFunctionBegin;
686   PetscCall(VecGetDM(originalv, &dm));
687   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   if (format == PETSC_VIEWER_NATIVE) {
691     if (dm->useNatural) {
692       if (dm->sfNatural) {
693         if (ishdf5) {
694 #if defined(PETSC_HAVE_HDF5)
695           Vec         v;
696           const char *vecname;
697 
698           PetscCall(DMGetGlobalVector(dm, &v));
699           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
700           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
701           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
702           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
703           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
704           PetscCall(DMRestoreGlobalVector(dm, &v));
705 #else
706           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
707 #endif
708         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
709       }
710     } else PetscCall(VecLoad_Default(originalv, viewer));
711   }
712   PetscFunctionReturn(0);
713 }
714 
715 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
716 {
717   PetscSection       coordSection;
718   Vec                coordinates;
719   DMLabel            depthLabel, celltypeLabel;
720   const char        *name[4];
721   const PetscScalar *a;
722   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
723 
724   PetscFunctionBegin;
725   PetscCall(DMGetDimension(dm, &dim));
726   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
727   PetscCall(DMGetCoordinateSection(dm, &coordSection));
728   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
729   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
730   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
731   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
732   PetscCall(VecGetArrayRead(coordinates, &a));
733   name[0]       = "vertex";
734   name[1]       = "edge";
735   name[dim - 1] = "face";
736   name[dim]     = "cell";
737   for (c = cStart; c < cEnd; ++c) {
738     PetscInt *closure = NULL;
739     PetscInt  closureSize, cl, ct;
740 
741     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
742     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
743     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
744     PetscCall(PetscViewerASCIIPushTab(viewer));
745     for (cl = 0; cl < closureSize * 2; cl += 2) {
746       PetscInt point = closure[cl], depth, dof, off, d, p;
747 
748       if ((point < pStart) || (point >= pEnd)) continue;
749       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
750       if (!dof) continue;
751       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
752       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
753       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
754       for (p = 0; p < dof / dim; ++p) {
755         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
756         for (d = 0; d < dim; ++d) {
757           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
758           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
759         }
760         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
761       }
762       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
763     }
764     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
765     PetscCall(PetscViewerASCIIPopTab(viewer));
766   }
767   PetscCall(VecRestoreArrayRead(coordinates, &a));
768   PetscFunctionReturn(0);
769 }
770 
771 typedef enum {
772   CS_CARTESIAN,
773   CS_POLAR,
774   CS_CYLINDRICAL,
775   CS_SPHERICAL
776 } CoordSystem;
777 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
778 
779 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
780 {
781   PetscInt i;
782 
783   PetscFunctionBegin;
784   if (dim > 3) {
785     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
786   } else {
787     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
788 
789     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
790     switch (cs) {
791     case CS_CARTESIAN:
792       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
793       break;
794     case CS_POLAR:
795       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
796       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
797       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
798       break;
799     case CS_CYLINDRICAL:
800       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
801       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
802       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
803       trcoords[2] = coords[2];
804       break;
805     case CS_SPHERICAL:
806       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
807       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
808       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
809       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
810       break;
811     }
812     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
813   }
814   PetscFunctionReturn(0);
815 }
816 
817 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
818 {
819   DM_Plex          *mesh = (DM_Plex *)dm->data;
820   DM                cdm, cdmCell;
821   PetscSection      coordSection, coordSectionCell;
822   Vec               coordinates, coordinatesCell;
823   PetscViewerFormat format;
824 
825   PetscFunctionBegin;
826   PetscCall(PetscViewerGetFormat(viewer, &format));
827   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
828     const char *name;
829     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
830     PetscInt    pStart, pEnd, p, numLabels, l;
831     PetscMPIInt rank, size;
832 
833     PetscCall(DMGetCoordinateDM(dm, &cdm));
834     PetscCall(DMGetCoordinateSection(dm, &coordSection));
835     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
836     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
837     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
838     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
839     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
840     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
841     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
842     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
843     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
844     PetscCall(DMGetDimension(dm, &dim));
845     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
846     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
847     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
848     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
849     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
850     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
851     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
852     for (p = pStart; p < pEnd; ++p) {
853       PetscInt dof, off, s;
854 
855       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
856       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
857       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
858     }
859     PetscCall(PetscViewerFlush(viewer));
860     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
861     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
862     for (p = pStart; p < pEnd; ++p) {
863       PetscInt dof, off, c;
864 
865       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
866       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
867       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]));
868     }
869     PetscCall(PetscViewerFlush(viewer));
870     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
871     if (coordSection && coordinates) {
872       CoordSystem        cs = CS_CARTESIAN;
873       const PetscScalar *array, *arrayCell = NULL;
874       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
875       PetscMPIInt        rank;
876       const char        *name;
877 
878       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
879       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
880       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
881       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
882       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
883       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
884       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
885       pStart = PetscMin(pvStart, pcStart);
886       pEnd   = PetscMax(pvEnd, pcEnd);
887       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
888       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
889       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
890       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
891 
892       PetscCall(VecGetArrayRead(coordinates, &array));
893       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
894       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
895       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
896       for (p = pStart; p < pEnd; ++p) {
897         PetscInt dof, off;
898 
899         if (p >= pvStart && p < pvEnd) {
900           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
901           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
902           if (dof) {
903             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
904             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
905             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
906           }
907         }
908         if (cdmCell && p >= pcStart && p < pcEnd) {
909           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
910           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
911           if (dof) {
912             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
913             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
914             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
915           }
916         }
917       }
918       PetscCall(PetscViewerFlush(viewer));
919       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
920       PetscCall(VecRestoreArrayRead(coordinates, &array));
921       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
922     }
923     PetscCall(DMGetNumLabels(dm, &numLabels));
924     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
925     for (l = 0; l < numLabels; ++l) {
926       DMLabel     label;
927       PetscBool   isdepth;
928       const char *name;
929 
930       PetscCall(DMGetLabelName(dm, l, &name));
931       PetscCall(PetscStrcmp(name, "depth", &isdepth));
932       if (isdepth) continue;
933       PetscCall(DMGetLabel(dm, name, &label));
934       PetscCall(DMLabelView(label, viewer));
935     }
936     if (size > 1) {
937       PetscSF sf;
938 
939       PetscCall(DMGetPointSF(dm, &sf));
940       PetscCall(PetscSFView(sf, viewer));
941     }
942     PetscCall(PetscViewerFlush(viewer));
943   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
944     const char  *name, *color;
945     const char  *defcolors[3]  = {"gray", "orange", "green"};
946     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
947     char         lname[PETSC_MAX_PATH_LEN];
948     PetscReal    scale      = 2.0;
949     PetscReal    tikzscale  = 1.0;
950     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
951     double       tcoords[3];
952     PetscScalar *coords;
953     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
954     PetscMPIInt  rank, size;
955     char       **names, **colors, **lcolors;
956     PetscBool    flg, lflg;
957     PetscBT      wp = NULL;
958     PetscInt     pEnd, pStart;
959 
960     PetscCall(DMGetCoordinateDM(dm, &cdm));
961     PetscCall(DMGetCoordinateSection(dm, &coordSection));
962     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
963     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
964     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
965     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
966     PetscCall(DMGetDimension(dm, &dim));
967     PetscCall(DMPlexGetDepth(dm, &depth));
968     PetscCall(DMGetNumLabels(dm, &numLabels));
969     numLabels  = PetscMax(numLabels, 10);
970     numColors  = 10;
971     numLColors = 10;
972     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
973     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
974     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
975     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
976     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
977     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
978     n = 4;
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
982     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
984     if (!useLabels) numLabels = 0;
985     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
986     if (!useColors) {
987       numColors = 3;
988       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
989     }
990     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
991     if (!useColors) {
992       numLColors = 4;
993       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
994     }
995     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
996     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
997     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
998     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
999     if (depth < dim) plotEdges = PETSC_FALSE;
1000     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1001 
1002     /* filter points with labelvalue != labeldefaultvalue */
1003     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1004     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1005     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1006     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1007     if (lflg) {
1008       DMLabel lbl;
1009 
1010       PetscCall(DMGetLabel(dm, lname, &lbl));
1011       if (lbl) {
1012         PetscInt val, defval;
1013 
1014         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1015         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1016         for (c = pStart; c < pEnd; c++) {
1017           PetscInt *closure = NULL;
1018           PetscInt  closureSize;
1019 
1020           PetscCall(DMLabelGetValue(lbl, c, &val));
1021           if (val == defval) continue;
1022 
1023           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1025           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1026         }
1027       }
1028     }
1029 
1030     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1031     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1032     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1033     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1034 \\documentclass[tikz]{standalone}\n\n\
1035 \\usepackage{pgflibraryshapes}\n\
1036 \\usetikzlibrary{backgrounds}\n\
1037 \\usetikzlibrary{arrows}\n\
1038 \\begin{document}\n"));
1039     if (size > 1) {
1040       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1041       for (p = 0; p < size; ++p) {
1042         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1043         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1044       }
1045       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1046     }
1047     if (drawHasse) {
1048       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1049 
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1060       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1061       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1062     }
1063     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1064 
1065     /* Plot vertices */
1066     PetscCall(VecGetArray(coordinates, &coords));
1067     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1068     for (v = vStart; v < vEnd; ++v) {
1069       PetscInt  off, dof, d;
1070       PetscBool isLabeled = PETSC_FALSE;
1071 
1072       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1073       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1074       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1075       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1076       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1077       for (d = 0; d < dof; ++d) {
1078         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1079         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1080       }
1081       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1082       if (dim == 3) {
1083         PetscReal tmp = tcoords[1];
1084         tcoords[1]    = tcoords[2];
1085         tcoords[2]    = -tmp;
1086       }
1087       for (d = 0; d < dof; ++d) {
1088         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1089         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1090       }
1091       if (drawHasse) color = colors[0 % numColors];
1092       else color = colors[rank % numColors];
1093       for (l = 0; l < numLabels; ++l) {
1094         PetscInt val;
1095         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1096         if (val >= 0) {
1097           color     = lcolors[l % numLColors];
1098           isLabeled = PETSC_TRUE;
1099           break;
1100         }
1101       }
1102       if (drawNumbers[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1104       } else if (drawColors[0]) {
1105         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1106       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1107     }
1108     PetscCall(VecRestoreArray(coordinates, &coords));
1109     PetscCall(PetscViewerFlush(viewer));
1110     /* Plot edges */
1111     if (plotEdges) {
1112       PetscCall(VecGetArray(coordinates, &coords));
1113       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1114       for (e = eStart; e < eEnd; ++e) {
1115         const PetscInt *cone;
1116         PetscInt        coneSize, offA, offB, dof, d;
1117 
1118         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1119         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1120         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1121         PetscCall(DMPlexGetCone(dm, e, &cone));
1122         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1123         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1124         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1125         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1126         for (d = 0; d < dof; ++d) {
1127           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1128           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1129         }
1130         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1131         if (dim == 3) {
1132           PetscReal tmp = tcoords[1];
1133           tcoords[1]    = tcoords[2];
1134           tcoords[2]    = -tmp;
1135         }
1136         for (d = 0; d < dof; ++d) {
1137           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1138           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1139         }
1140         if (drawHasse) color = colors[1 % numColors];
1141         else color = colors[rank % numColors];
1142         for (l = 0; l < numLabels; ++l) {
1143           PetscInt val;
1144           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1145           if (val >= 0) {
1146             color = lcolors[l % numLColors];
1147             break;
1148           }
1149         }
1150         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1151       }
1152       PetscCall(VecRestoreArray(coordinates, &coords));
1153       PetscCall(PetscViewerFlush(viewer));
1154       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1155     }
1156     /* Plot cells */
1157     if (dim == 3 || !drawNumbers[1]) {
1158       for (e = eStart; e < eEnd; ++e) {
1159         const PetscInt *cone;
1160 
1161         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1162         color = colors[rank % numColors];
1163         for (l = 0; l < numLabels; ++l) {
1164           PetscInt val;
1165           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1166           if (val >= 0) {
1167             color = lcolors[l % numLColors];
1168             break;
1169           }
1170         }
1171         PetscCall(DMPlexGetCone(dm, e, &cone));
1172         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1173       }
1174     } else {
1175       DMPolytopeType ct;
1176 
1177       /* Drawing a 2D polygon */
1178       for (c = cStart; c < cEnd; ++c) {
1179         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1180         PetscCall(DMPlexGetCellType(dm, c, &ct));
1181         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1182           const PetscInt *cone;
1183           PetscInt        coneSize, e;
1184 
1185           PetscCall(DMPlexGetCone(dm, c, &cone));
1186           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1187           for (e = 0; e < coneSize; ++e) {
1188             const PetscInt *econe;
1189 
1190             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1191             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));
1192           }
1193         } else {
1194           PetscInt *closure = NULL;
1195           PetscInt  closureSize, Nv = 0, v;
1196 
1197           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1198           for (p = 0; p < closureSize * 2; p += 2) {
1199             const PetscInt point = closure[p];
1200 
1201             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1202           }
1203           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1204           for (v = 0; v <= Nv; ++v) {
1205             const PetscInt vertex = closure[v % Nv];
1206 
1207             if (v > 0) {
1208               if (plotEdges) {
1209                 const PetscInt *edge;
1210                 PetscInt        endpoints[2], ne;
1211 
1212                 endpoints[0] = closure[v - 1];
1213                 endpoints[1] = vertex;
1214                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1215                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1216                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1217                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1218               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1219             }
1220             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1221           }
1222           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1223           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224         }
1225       }
1226     }
1227     for (c = cStart; c < cEnd; ++c) {
1228       double             ccoords[3] = {0.0, 0.0, 0.0};
1229       PetscBool          isLabeled  = PETSC_FALSE;
1230       PetscScalar       *cellCoords = NULL;
1231       const PetscScalar *array;
1232       PetscInt           numCoords, cdim, d;
1233       PetscBool          isDG;
1234 
1235       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1236       PetscCall(DMGetCoordinateDim(dm, &cdim));
1237       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1238       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1239       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1240       for (p = 0; p < numCoords / cdim; ++p) {
1241         for (d = 0; d < cdim; ++d) {
1242           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1243           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1244         }
1245         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1246         if (cdim == 3) {
1247           PetscReal tmp = tcoords[1];
1248           tcoords[1]    = tcoords[2];
1249           tcoords[2]    = -tmp;
1250         }
1251         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1252       }
1253       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1254       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1255       for (d = 0; d < cdim; ++d) {
1256         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1258       }
1259       if (drawHasse) color = colors[depth % numColors];
1260       else color = colors[rank % numColors];
1261       for (l = 0; l < numLabels; ++l) {
1262         PetscInt val;
1263         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1264         if (val >= 0) {
1265           color     = lcolors[l % numLColors];
1266           isLabeled = PETSC_TRUE;
1267           break;
1268         }
1269       }
1270       if (drawNumbers[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1272       } else if (drawColors[dim]) {
1273         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1274       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1275     }
1276     if (drawHasse) {
1277       color = colors[depth % numColors];
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1283 
1284       color = colors[1 % numColors];
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1290 
1291       color = colors[0 % numColors];
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1297 
1298       for (p = pStart; p < pEnd; ++p) {
1299         const PetscInt *cone;
1300         PetscInt        coneSize, cp;
1301 
1302         PetscCall(DMPlexGetCone(dm, p, &cone));
1303         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1304         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1305       }
1306     }
1307     PetscCall(PetscViewerFlush(viewer));
1308     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1309     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1310     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1311     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1312     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1313     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1314     PetscCall(PetscFree3(names, colors, lcolors));
1315     PetscCall(PetscBTDestroy(&wp));
1316   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1317     Vec                    cown, acown;
1318     VecScatter             sct;
1319     ISLocalToGlobalMapping g2l;
1320     IS                     gid, acis;
1321     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1322     MPI_Group              ggroup, ngroup;
1323     PetscScalar           *array, nid;
1324     const PetscInt        *idxs;
1325     PetscInt              *idxs2, *start, *adjacency, *work;
1326     PetscInt64             lm[3], gm[3];
1327     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1328     PetscMPIInt            d1, d2, rank;
1329 
1330     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1331     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1332 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1333     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1334 #endif
1335     if (ncomm != MPI_COMM_NULL) {
1336       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1337       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1338       d1 = 0;
1339       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1340       nid = d2;
1341       PetscCallMPI(MPI_Group_free(&ggroup));
1342       PetscCallMPI(MPI_Group_free(&ngroup));
1343       PetscCallMPI(MPI_Comm_free(&ncomm));
1344     } else nid = 0.0;
1345 
1346     /* Get connectivity */
1347     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1348     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1349 
1350     /* filter overlapped local cells */
1351     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1352     PetscCall(ISGetIndices(gid, &idxs));
1353     PetscCall(ISGetLocalSize(gid, &cum));
1354     PetscCall(PetscMalloc1(cum, &idxs2));
1355     for (c = cStart, cum = 0; c < cEnd; c++) {
1356       if (idxs[c - cStart] < 0) continue;
1357       idxs2[cum++] = idxs[c - cStart];
1358     }
1359     PetscCall(ISRestoreIndices(gid, &idxs));
1360     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1361     PetscCall(ISDestroy(&gid));
1362     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1363 
1364     /* support for node-aware cell locality */
1365     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1366     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1367     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1368     PetscCall(VecGetArray(cown, &array));
1369     for (c = 0; c < numVertices; c++) array[c] = nid;
1370     PetscCall(VecRestoreArray(cown, &array));
1371     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1372     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1373     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1374     PetscCall(ISDestroy(&acis));
1375     PetscCall(VecScatterDestroy(&sct));
1376     PetscCall(VecDestroy(&cown));
1377 
1378     /* compute edgeCut */
1379     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1380     PetscCall(PetscMalloc1(cum, &work));
1381     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1382     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1383     PetscCall(ISDestroy(&gid));
1384     PetscCall(VecGetArray(acown, &array));
1385     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1386       PetscInt totl;
1387 
1388       totl = start[c + 1] - start[c];
1389       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1390       for (i = 0; i < totl; i++) {
1391         if (work[i] < 0) {
1392           ect += 1;
1393           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1394         }
1395       }
1396     }
1397     PetscCall(PetscFree(work));
1398     PetscCall(VecRestoreArray(acown, &array));
1399     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1400     lm[1] = -numVertices;
1401     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1402     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1403     lm[0] = ect;                     /* edgeCut */
1404     lm[1] = ectn;                    /* node-aware edgeCut */
1405     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1406     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1407     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1408 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1410 #else
1411     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1412 #endif
1413     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1414     PetscCall(PetscFree(start));
1415     PetscCall(PetscFree(adjacency));
1416     PetscCall(VecDestroy(&acown));
1417   } else {
1418     const char    *name;
1419     PetscInt      *sizes, *hybsizes, *ghostsizes;
1420     PetscInt       locDepth, depth, cellHeight, dim, d;
1421     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1422     PetscInt       numLabels, l, maxSize = 17;
1423     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1424     MPI_Comm       comm;
1425     PetscMPIInt    size, rank;
1426 
1427     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1428     PetscCallMPI(MPI_Comm_size(comm, &size));
1429     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1430     PetscCall(DMGetDimension(dm, &dim));
1431     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1432     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1433     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1434     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1435     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1436     PetscCall(DMPlexGetDepth(dm, &locDepth));
1437     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1438     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1439     gcNum = gcEnd - gcStart;
1440     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1441     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1442     for (d = 0; d <= depth; d++) {
1443       PetscInt Nc[2] = {0, 0}, ict;
1444 
1445       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1446       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1447       ict = ct0;
1448       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1449       ct0 = (DMPolytopeType)ict;
1450       for (p = pStart; p < pEnd; ++p) {
1451         DMPolytopeType ct;
1452 
1453         PetscCall(DMPlexGetCellType(dm, p, &ct));
1454         if (ct == ct0) ++Nc[0];
1455         else ++Nc[1];
1456       }
1457       if (size < maxSize) {
1458         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1459         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1460         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1461         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1462         for (p = 0; p < size; ++p) {
1463           if (rank == 0) {
1464             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1465             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1466             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1467           }
1468         }
1469       } else {
1470         PetscInt locMinMax[2];
1471 
1472         locMinMax[0] = Nc[0] + Nc[1];
1473         locMinMax[1] = Nc[0] + Nc[1];
1474         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1475         locMinMax[0] = Nc[1];
1476         locMinMax[1] = Nc[1];
1477         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1478         if (d == depth) {
1479           locMinMax[0] = gcNum;
1480           locMinMax[1] = gcNum;
1481           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1482         }
1483         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1484         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1485         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1486         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1487       }
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1489     }
1490     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1491     {
1492       const PetscReal *maxCell;
1493       const PetscReal *L;
1494       PetscBool        localized;
1495 
1496       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1497       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1498       if (L || localized) {
1499         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1500         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1501         if (L) {
1502           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1503           for (d = 0; d < dim; ++d) {
1504             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1505             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1506           }
1507           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1508         }
1509         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1510         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1511       }
1512     }
1513     PetscCall(DMGetNumLabels(dm, &numLabels));
1514     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1515     for (l = 0; l < numLabels; ++l) {
1516       DMLabel         label;
1517       const char     *name;
1518       IS              valueIS;
1519       const PetscInt *values;
1520       PetscInt        numValues, v;
1521 
1522       PetscCall(DMGetLabelName(dm, l, &name));
1523       PetscCall(DMGetLabel(dm, name, &label));
1524       PetscCall(DMLabelGetNumValues(label, &numValues));
1525       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1526       PetscCall(DMLabelGetValueIS(label, &valueIS));
1527       PetscCall(ISGetIndices(valueIS, &values));
1528       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1529       for (v = 0; v < numValues; ++v) {
1530         PetscInt size;
1531 
1532         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1533         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1534         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1535       }
1536       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1537       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1538       PetscCall(ISRestoreIndices(valueIS, &values));
1539       PetscCall(ISDestroy(&valueIS));
1540     }
1541     {
1542       char    **labelNames;
1543       PetscInt  Nl = numLabels;
1544       PetscBool flg;
1545 
1546       PetscCall(PetscMalloc1(Nl, &labelNames));
1547       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1548       for (l = 0; l < Nl; ++l) {
1549         DMLabel label;
1550 
1551         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1552         if (flg) {
1553           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1554           PetscCall(DMLabelView(label, viewer));
1555         }
1556         PetscCall(PetscFree(labelNames[l]));
1557       }
1558       PetscCall(PetscFree(labelNames));
1559     }
1560     /* If no fields are specified, people do not want to see adjacency */
1561     if (dm->Nf) {
1562       PetscInt f;
1563 
1564       for (f = 0; f < dm->Nf; ++f) {
1565         const char *name;
1566 
1567         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1568         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1569         PetscCall(PetscViewerASCIIPushTab(viewer));
1570         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1571         if (dm->fields[f].adjacency[0]) {
1572           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1573           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1574         } else {
1575           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1576           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1577         }
1578         PetscCall(PetscViewerASCIIPopTab(viewer));
1579       }
1580     }
1581     PetscCall(DMGetCoarseDM(dm, &cdm));
1582     if (cdm) {
1583       PetscCall(PetscViewerASCIIPushTab(viewer));
1584       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1585       PetscCall(DMPlexView_Ascii(cdm, viewer));
1586       PetscCall(PetscViewerASCIIPopTab(viewer));
1587     }
1588   }
1589   PetscFunctionReturn(0);
1590 }
1591 
1592 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1593 {
1594   DMPolytopeType ct;
1595   PetscMPIInt    rank;
1596   PetscInt       cdim;
1597 
1598   PetscFunctionBegin;
1599   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1600   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1601   PetscCall(DMGetCoordinateDim(dm, &cdim));
1602   switch (ct) {
1603   case DM_POLYTOPE_SEGMENT:
1604   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1605     switch (cdim) {
1606     case 1: {
1607       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1608       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1609 
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1611       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1612       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1613     } break;
1614     case 2: {
1615       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1616       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1617       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1618 
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1620       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));
1621       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));
1622     } break;
1623     default:
1624       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1625     }
1626     break;
1627   case DM_POLYTOPE_TRIANGLE:
1628     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));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1630     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1631     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1632     break;
1633   case DM_POLYTOPE_QUADRILATERAL:
1634     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));
1635     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));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1638     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1639     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1640     break;
1641   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1642     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));
1643     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1646     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1647     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1648     break;
1649   case DM_POLYTOPE_FV_GHOST:
1650     break;
1651   default:
1652     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1653   }
1654   PetscFunctionReturn(0);
1655 }
1656 
1657 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1658 {
1659   DMPolytopeType ct;
1660   PetscReal      centroid[2] = {0., 0.};
1661   PetscMPIInt    rank;
1662   PetscInt       fillColor, v, e, d;
1663 
1664   PetscFunctionBegin;
1665   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1666   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1667   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1668   switch (ct) {
1669   case DM_POLYTOPE_TRIANGLE: {
1670     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1671 
1672     for (v = 0; v < 3; ++v) {
1673       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1674       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1675     }
1676     for (e = 0; e < 3; ++e) {
1677       refCoords[0] = refVertices[e * 2 + 0];
1678       refCoords[1] = refVertices[e * 2 + 1];
1679       for (d = 1; d <= edgeDiv; ++d) {
1680         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1681         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1682       }
1683       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1684       for (d = 0; d < edgeDiv; ++d) {
1685         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));
1686         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1687       }
1688     }
1689   } break;
1690   default:
1691     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1692   }
1693   PetscFunctionReturn(0);
1694 }
1695 
1696 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1697 {
1698   PetscDraw          draw;
1699   DM                 cdm;
1700   PetscSection       coordSection;
1701   Vec                coordinates;
1702   const PetscScalar *coords;
1703   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1704   PetscReal         *refCoords, *edgeCoords;
1705   PetscBool          isnull, drawAffine = PETSC_TRUE;
1706   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1707 
1708   PetscFunctionBegin;
1709   PetscCall(DMGetCoordinateDim(dm, &dim));
1710   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1711   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1712   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1713   PetscCall(DMGetCoordinateDM(dm, &cdm));
1714   PetscCall(DMGetLocalSection(cdm, &coordSection));
1715   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1716   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1717   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1718 
1719   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1720   PetscCall(PetscDrawIsNull(draw, &isnull));
1721   if (isnull) PetscFunctionReturn(0);
1722   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1723 
1724   PetscCall(VecGetLocalSize(coordinates, &N));
1725   PetscCall(VecGetArrayRead(coordinates, &coords));
1726   for (c = 0; c < N; c += dim) {
1727     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1728     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1729     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1730     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1731   }
1732   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1733   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1734   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1735   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1736   PetscCall(PetscDrawClear(draw));
1737 
1738   for (c = cStart; c < cEnd; ++c) {
1739     PetscScalar *coords = NULL;
1740     PetscInt     numCoords;
1741 
1742     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1743     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1744     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1745     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1746   }
1747   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1748   PetscCall(PetscDrawFlush(draw));
1749   PetscCall(PetscDrawPause(draw));
1750   PetscCall(PetscDrawSave(draw));
1751   PetscFunctionReturn(0);
1752 }
1753 
1754 #if defined(PETSC_HAVE_EXODUSII)
1755   #include <exodusII.h>
1756   #include <petscviewerexodusii.h>
1757 #endif
1758 
1759 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1760 {
1761   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1762   char      name[PETSC_MAX_PATH_LEN];
1763 
1764   PetscFunctionBegin;
1765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1766   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1768   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1769   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1771   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1774   if (iascii) {
1775     PetscViewerFormat format;
1776     PetscCall(PetscViewerGetFormat(viewer, &format));
1777     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1778     else PetscCall(DMPlexView_Ascii(dm, viewer));
1779   } else if (ishdf5) {
1780 #if defined(PETSC_HAVE_HDF5)
1781     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1782 #else
1783     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1784 #endif
1785   } else if (isvtk) {
1786     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1787   } else if (isdraw) {
1788     PetscCall(DMPlexView_Draw(dm, viewer));
1789   } else if (isglvis) {
1790     PetscCall(DMPlexView_GLVis(dm, viewer));
1791 #if defined(PETSC_HAVE_EXODUSII)
1792   } else if (isexodus) {
1793     /*
1794       exodusII requires that all sets be part of exactly one cell set.
1795       If the dm does not have a "Cell Sets" label defined, we create one
1796       with ID 1, containig all cells.
1797       Note that if the Cell Sets label is defined but does not cover all cells,
1798       we may still have a problem. This should probably be checked here or in the viewer;
1799     */
1800     PetscInt numCS;
1801     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1802     if (!numCS) {
1803       PetscInt cStart, cEnd, c;
1804       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1805       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1806       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1807     }
1808     PetscCall(DMView_PlexExodusII(dm, viewer));
1809 #endif
1810 #if defined(PETSC_HAVE_CGNS)
1811   } else if (iscgns) {
1812     PetscCall(DMView_PlexCGNS(dm, viewer));
1813 #endif
1814   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1815   /* Optionally view the partition */
1816   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1817   if (flg) {
1818     Vec ranks;
1819     PetscCall(DMPlexCreateRankField(dm, &ranks));
1820     PetscCall(VecView(ranks, viewer));
1821     PetscCall(VecDestroy(&ranks));
1822   }
1823   /* Optionally view a label */
1824   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1825   if (flg) {
1826     DMLabel label;
1827     Vec     val;
1828 
1829     PetscCall(DMGetLabel(dm, name, &label));
1830     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1831     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1832     PetscCall(VecView(val, viewer));
1833     PetscCall(VecDestroy(&val));
1834   }
1835   PetscFunctionReturn(0);
1836 }
1837 
1838 /*@
1839   DMPlexTopologyView - Saves a DMPlex topology into a file
1840 
1841   Collective on DM
1842 
1843   Input Parameters:
1844 + dm                - The DM whose topology is to be saved
1845 - viewer            - The PetscViewer for saving
1846 
1847   Level: advanced
1848 
1849 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1850 @*/
1851 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1852 {
1853   PetscBool ishdf5;
1854 
1855   PetscFunctionBegin;
1856   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1857   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1858   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1859   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1860   if (ishdf5) {
1861 #if defined(PETSC_HAVE_HDF5)
1862     PetscViewerFormat format;
1863     PetscCall(PetscViewerGetFormat(viewer, &format));
1864     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1865       IS globalPointNumbering;
1866 
1867       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1868       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1869       PetscCall(ISDestroy(&globalPointNumbering));
1870     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1871 #else
1872     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1873 #endif
1874   }
1875   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1876   PetscFunctionReturn(0);
1877 }
1878 
1879 /*@
1880   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1881 
1882   Collective on DM
1883 
1884   Input Parameters:
1885 + dm     - The DM whose coordinates are to be saved
1886 - viewer - The PetscViewer for saving
1887 
1888   Level: advanced
1889 
1890 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1891 @*/
1892 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1893 {
1894   PetscBool ishdf5;
1895 
1896   PetscFunctionBegin;
1897   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1898   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1899   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1900   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1901   if (ishdf5) {
1902 #if defined(PETSC_HAVE_HDF5)
1903     PetscViewerFormat format;
1904     PetscCall(PetscViewerGetFormat(viewer, &format));
1905     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1906       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1907     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1908 #else
1909     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1910 #endif
1911   }
1912   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1913   PetscFunctionReturn(0);
1914 }
1915 
1916 /*@
1917   DMPlexLabelsView - Saves DMPlex labels into a file
1918 
1919   Collective on DM
1920 
1921   Input Parameters:
1922 + dm     - The DM whose labels are to be saved
1923 - viewer - The PetscViewer for saving
1924 
1925   Level: advanced
1926 
1927 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1928 @*/
1929 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1930 {
1931   PetscBool ishdf5;
1932 
1933   PetscFunctionBegin;
1934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1935   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1936   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1937   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1938   if (ishdf5) {
1939 #if defined(PETSC_HAVE_HDF5)
1940     IS                globalPointNumbering;
1941     PetscViewerFormat format;
1942 
1943     PetscCall(PetscViewerGetFormat(viewer, &format));
1944     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1945       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1946       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1947       PetscCall(ISDestroy(&globalPointNumbering));
1948     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1949 #else
1950     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1951 #endif
1952   }
1953   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1954   PetscFunctionReturn(0);
1955 }
1956 
1957 /*@
1958   DMPlexSectionView - Saves a section associated with a DMPlex
1959 
1960   Collective on DM
1961 
1962   Input Parameters:
1963 + dm         - The DM that contains the topology on which the section to be saved is defined
1964 . viewer     - The PetscViewer for saving
1965 - sectiondm  - The DM that contains the section to be saved
1966 
1967   Level: advanced
1968 
1969   Notes:
1970   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.
1971 
1972   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.
1973 
1974 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1975 @*/
1976 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1977 {
1978   PetscBool ishdf5;
1979 
1980   PetscFunctionBegin;
1981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1982   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1983   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1984   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1985   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1986   if (ishdf5) {
1987 #if defined(PETSC_HAVE_HDF5)
1988     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1989 #else
1990     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1991 #endif
1992   }
1993   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1994   PetscFunctionReturn(0);
1995 }
1996 
1997 /*@
1998   DMPlexGlobalVectorView - Saves a global vector
1999 
2000   Collective on DM
2001 
2002   Input Parameters:
2003 + dm        - The DM that represents the topology
2004 . viewer    - The PetscViewer to save data with
2005 . sectiondm - The DM that contains the global section on which vec is defined
2006 - vec       - The global vector to be saved
2007 
2008   Level: advanced
2009 
2010   Notes:
2011   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.
2012 
2013   Typical calling sequence
2014 $       DMCreate(PETSC_COMM_WORLD, &dm);
2015 $       DMSetType(dm, DMPLEX);
2016 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2017 $       DMClone(dm, &sectiondm);
2018 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2019 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2020 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2021 $       PetscSectionSetChart(section, pStart, pEnd);
2022 $       PetscSectionSetUp(section);
2023 $       DMSetLocalSection(sectiondm, section);
2024 $       PetscSectionDestroy(&section);
2025 $       DMGetGlobalVector(sectiondm, &vec);
2026 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2027 $       DMPlexTopologyView(dm, viewer);
2028 $       DMPlexSectionView(dm, viewer, sectiondm);
2029 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2030 $       DMRestoreGlobalVector(sectiondm, &vec);
2031 $       DMDestroy(&sectiondm);
2032 $       DMDestroy(&dm);
2033 
2034 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2035 @*/
2036 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2037 {
2038   PetscBool ishdf5;
2039 
2040   PetscFunctionBegin;
2041   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2042   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2043   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2044   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2045   /* Check consistency */
2046   {
2047     PetscSection section;
2048     PetscBool    includesConstraints;
2049     PetscInt     m, m1;
2050 
2051     PetscCall(VecGetLocalSize(vec, &m1));
2052     PetscCall(DMGetGlobalSection(sectiondm, &section));
2053     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2054     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2055     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2056     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2057   }
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2059   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2060   if (ishdf5) {
2061 #if defined(PETSC_HAVE_HDF5)
2062     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2063 #else
2064     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2065 #endif
2066   }
2067   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2068   PetscFunctionReturn(0);
2069 }
2070 
2071 /*@
2072   DMPlexLocalVectorView - Saves a local vector
2073 
2074   Collective on DM
2075 
2076   Input Parameters:
2077 + dm        - The DM that represents the topology
2078 . viewer    - The PetscViewer to save data with
2079 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2080 - vec       - The local vector to be saved
2081 
2082   Level: advanced
2083 
2084   Notes:
2085   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.
2086 
2087   Typical calling sequence
2088 $       DMCreate(PETSC_COMM_WORLD, &dm);
2089 $       DMSetType(dm, DMPLEX);
2090 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2091 $       DMClone(dm, &sectiondm);
2092 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2093 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2094 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2095 $       PetscSectionSetChart(section, pStart, pEnd);
2096 $       PetscSectionSetUp(section);
2097 $       DMSetLocalSection(sectiondm, section);
2098 $       DMGetLocalVector(sectiondm, &vec);
2099 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2100 $       DMPlexTopologyView(dm, viewer);
2101 $       DMPlexSectionView(dm, viewer, sectiondm);
2102 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2103 $       DMRestoreLocalVector(sectiondm, &vec);
2104 $       DMDestroy(&sectiondm);
2105 $       DMDestroy(&dm);
2106 
2107 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2108 @*/
2109 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2110 {
2111   PetscBool ishdf5;
2112 
2113   PetscFunctionBegin;
2114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2115   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2116   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2117   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2118   /* Check consistency */
2119   {
2120     PetscSection section;
2121     PetscBool    includesConstraints;
2122     PetscInt     m, m1;
2123 
2124     PetscCall(VecGetLocalSize(vec, &m1));
2125     PetscCall(DMGetLocalSection(sectiondm, &section));
2126     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2127     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2128     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2129     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2130   }
2131   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2132   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2133   if (ishdf5) {
2134 #if defined(PETSC_HAVE_HDF5)
2135     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2136 #else
2137     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2138 #endif
2139   }
2140   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2141   PetscFunctionReturn(0);
2142 }
2143 
2144 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2145 {
2146   PetscBool ishdf5;
2147 
2148   PetscFunctionBegin;
2149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2150   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2151   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2152   if (ishdf5) {
2153 #if defined(PETSC_HAVE_HDF5)
2154     PetscViewerFormat format;
2155     PetscCall(PetscViewerGetFormat(viewer, &format));
2156     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2157       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2158     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2159       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2160     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2161     PetscFunctionReturn(0);
2162 #else
2163     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2164 #endif
2165   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2166 }
2167 
2168 /*@
2169   DMPlexTopologyLoad - Loads a topology into a DMPlex
2170 
2171   Collective on DM
2172 
2173   Input Parameters:
2174 + dm                - The DM into which the topology is loaded
2175 - viewer            - The PetscViewer for the saved topology
2176 
2177   Output Parameters:
2178 . 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
2179 
2180   Level: advanced
2181 
2182 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2183 @*/
2184 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2185 {
2186   PetscBool ishdf5;
2187 
2188   PetscFunctionBegin;
2189   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2190   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2191   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2192   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2193   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2194   if (ishdf5) {
2195 #if defined(PETSC_HAVE_HDF5)
2196     PetscViewerFormat format;
2197     PetscCall(PetscViewerGetFormat(viewer, &format));
2198     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2199       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2200     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2201 #else
2202     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2203 #endif
2204   }
2205   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2206   PetscFunctionReturn(0);
2207 }
2208 
2209 /*@
2210   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2211 
2212   Collective on DM
2213 
2214   Input Parameters:
2215 + dm     - The DM into which the coordinates are loaded
2216 . viewer - The PetscViewer for the saved coordinates
2217 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2218 
2219   Level: advanced
2220 
2221 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2222 @*/
2223 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2224 {
2225   PetscBool ishdf5;
2226 
2227   PetscFunctionBegin;
2228   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2229   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2230   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2231   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2232   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2233   if (ishdf5) {
2234 #if defined(PETSC_HAVE_HDF5)
2235     PetscViewerFormat format;
2236     PetscCall(PetscViewerGetFormat(viewer, &format));
2237     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2238       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2239     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2240 #else
2241     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2242 #endif
2243   }
2244   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2245   PetscFunctionReturn(0);
2246 }
2247 
2248 /*@
2249   DMPlexLabelsLoad - Loads labels into a DMPlex
2250 
2251   Collective on DM
2252 
2253   Input Parameters:
2254 + dm     - The DM into which the labels are loaded
2255 . viewer - The PetscViewer for the saved labels
2256 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2257 
2258   Level: advanced
2259 
2260   Notes:
2261   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2262 
2263 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2264 @*/
2265 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2266 {
2267   PetscBool ishdf5;
2268 
2269   PetscFunctionBegin;
2270   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2271   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2272   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2273   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2274   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2275   if (ishdf5) {
2276 #if defined(PETSC_HAVE_HDF5)
2277     PetscViewerFormat format;
2278 
2279     PetscCall(PetscViewerGetFormat(viewer, &format));
2280     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2281       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2282     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2283 #else
2284     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2285 #endif
2286   }
2287   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2288   PetscFunctionReturn(0);
2289 }
2290 
2291 /*@
2292   DMPlexSectionLoad - Loads section into a DMPlex
2293 
2294   Collective on DM
2295 
2296   Input Parameters:
2297 + dm          - The DM that represents the topology
2298 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2299 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2300 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2301 
2302   Output Parameters
2303 + 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)
2304 - 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)
2305 
2306   Level: advanced
2307 
2308   Notes:
2309   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.
2310 
2311   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.
2312 
2313   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.
2314 
2315   Example using 2 processes:
2316 $  NX (number of points on dm): 4
2317 $  sectionA                   : the on-disk section
2318 $  vecA                       : a vector associated with sectionA
2319 $  sectionB                   : sectiondm's local section constructed in this function
2320 $  vecB (local)               : a vector associated with sectiondm's local section
2321 $  vecB (global)              : a vector associated with sectiondm's global section
2322 $
2323 $                                     rank 0    rank 1
2324 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2325 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2326 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2327 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2328 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2329 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2330 $  sectionB->atlasDof             :     1 0 1 | 1 3
2331 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2332 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2333 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2334 $
2335 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2336 
2337 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2338 @*/
2339 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2340 {
2341   PetscBool ishdf5;
2342 
2343   PetscFunctionBegin;
2344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2345   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2346   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2347   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2348   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2349   if (localDofSF) PetscValidPointer(localDofSF, 6);
2350   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2351   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2352   if (ishdf5) {
2353 #if defined(PETSC_HAVE_HDF5)
2354     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2355 #else
2356     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2357 #endif
2358   }
2359   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2360   PetscFunctionReturn(0);
2361 }
2362 
2363 /*@
2364   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2365 
2366   Collective on DM
2367 
2368   Input Parameters:
2369 + dm        - The DM that represents the topology
2370 . viewer    - The PetscViewer that represents the on-disk vector data
2371 . sectiondm - The DM that contains the global section on which vec is defined
2372 . sf        - The SF that migrates the on-disk vector data into vec
2373 - vec       - The global vector to set values of
2374 
2375   Level: advanced
2376 
2377   Notes:
2378   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.
2379 
2380   Typical calling sequence
2381 $       DMCreate(PETSC_COMM_WORLD, &dm);
2382 $       DMSetType(dm, DMPLEX);
2383 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2384 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2385 $       DMClone(dm, &sectiondm);
2386 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2387 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2388 $       DMGetGlobalVector(sectiondm, &vec);
2389 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2390 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2391 $       DMRestoreGlobalVector(sectiondm, &vec);
2392 $       PetscSFDestroy(&gsf);
2393 $       PetscSFDestroy(&sfX);
2394 $       DMDestroy(&sectiondm);
2395 $       DMDestroy(&dm);
2396 
2397 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2398 @*/
2399 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2407   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetGlobalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(0);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective on DM
2439 
2440   Input Parameters:
2441 + dm        - The DM that represents the topology
2442 . viewer    - The PetscViewer that represents the on-disk vector data
2443 . sectiondm - The DM that contains the local section on which vec is defined
2444 . sf        - The SF that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   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.
2451 
2452   Typical calling sequence
2453 $       DMCreate(PETSC_COMM_WORLD, &dm);
2454 $       DMSetType(dm, DMPLEX);
2455 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2456 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2457 $       DMClone(dm, &sectiondm);
2458 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2459 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2460 $       DMGetLocalVector(sectiondm, &vec);
2461 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2462 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2463 $       DMRestoreLocalVector(sectiondm, &vec);
2464 $       PetscSFDestroy(&lsf);
2465 $       PetscSFDestroy(&sfX);
2466 $       DMDestroy(&sectiondm);
2467 $       DMDestroy(&dm);
2468 
2469 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2470 @*/
2471 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2472 {
2473   PetscBool ishdf5;
2474 
2475   PetscFunctionBegin;
2476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2477   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2478   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2479   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2480   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2481   /* Check consistency */
2482   {
2483     PetscSection section;
2484     PetscBool    includesConstraints;
2485     PetscInt     m, m1;
2486 
2487     PetscCall(VecGetLocalSize(vec, &m1));
2488     PetscCall(DMGetLocalSection(sectiondm, &section));
2489     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2490     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2491     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2492     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2493   }
2494   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2495   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2496   if (ishdf5) {
2497 #if defined(PETSC_HAVE_HDF5)
2498     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2499 #else
2500     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2501 #endif
2502   }
2503   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2504   PetscFunctionReturn(0);
2505 }
2506 
2507 PetscErrorCode DMDestroy_Plex(DM dm)
2508 {
2509   DM_Plex *mesh = (DM_Plex *)dm->data;
2510 
2511   PetscFunctionBegin;
2512   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2513   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2514   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2525   if (--mesh->refct > 0) PetscFunctionReturn(0);
2526   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2527   PetscCall(PetscFree(mesh->cones));
2528   PetscCall(PetscFree(mesh->coneOrientations));
2529   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2530   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2531   PetscCall(PetscFree(mesh->supports));
2532   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2533   PetscCall(PetscFree(mesh->facesTmp));
2534   PetscCall(PetscFree(mesh->tetgenOpts));
2535   PetscCall(PetscFree(mesh->triangleOpts));
2536   PetscCall(PetscFree(mesh->transformType));
2537   PetscCall(PetscFree(mesh->distributionName));
2538   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2539   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2540   PetscCall(ISDestroy(&mesh->subpointIS));
2541   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2542   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2543   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2544   PetscCall(ISDestroy(&mesh->anchorIS));
2545   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2546   PetscCall(PetscFree(mesh->parents));
2547   PetscCall(PetscFree(mesh->childIDs));
2548   PetscCall(PetscSectionDestroy(&mesh->childSection));
2549   PetscCall(PetscFree(mesh->children));
2550   PetscCall(DMDestroy(&mesh->referenceTree));
2551   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2552   PetscCall(PetscFree(mesh->neighbors));
2553   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2554   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2555   PetscCall(PetscFree(mesh));
2556   PetscFunctionReturn(0);
2557 }
2558 
2559 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2560 {
2561   PetscSection           sectionGlobal;
2562   PetscInt               bs = -1, mbs;
2563   PetscInt               localSize, localStart = 0;
2564   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2565   MatType                mtype;
2566   ISLocalToGlobalMapping ltog;
2567 
2568   PetscFunctionBegin;
2569   PetscCall(MatInitializePackage());
2570   mtype = dm->mattype;
2571   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2572   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2573   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2574   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2575   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2576   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2577   PetscCall(MatSetType(*J, mtype));
2578   PetscCall(MatSetFromOptions(*J));
2579   PetscCall(MatGetBlockSize(*J, &mbs));
2580   if (mbs > 1) bs = mbs;
2581   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2582   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2583   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2584   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2585   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2586   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2587   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2588   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2589   if (!isShell) {
2590     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2591     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2592     PetscInt  pStart, pEnd, p, dof, cdof;
2593 
2594     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2595 
2596     PetscCall(PetscCalloc1(localSize, &pblocks));
2597     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2598     for (p = pStart; p < pEnd; ++p) {
2599       PetscInt bdof, offset;
2600 
2601       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2602       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2603       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2604       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2605       dof  = dof < 0 ? -(dof + 1) : dof;
2606       bdof = cdof && (dof - cdof) ? 1 : dof;
2607       if (dof) {
2608         if (bs < 0) {
2609           bs = bdof;
2610         } else if (bs != bdof) {
2611           bs = 1;
2612         }
2613       }
2614     }
2615     /* Must have same blocksize on all procs (some might have no points) */
2616     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2617     bsLocal[1] = bs;
2618     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2619     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2620     else bs = bsMinMax[0];
2621     bs = PetscMax(1, bs);
2622     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2623     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2624       PetscCall(MatSetBlockSize(*J, bs));
2625       PetscCall(MatSetUp(*J));
2626     } else {
2627       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2628       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2629       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2630     }
2631     { // Consolidate blocks
2632       PetscInt nblocks = 0;
2633       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2634         if (pblocks[i] == 0) continue;
2635         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2636         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]);
2637       }
2638       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2639     }
2640     PetscCall(PetscFree(pblocks));
2641   }
2642   PetscCall(MatSetDM(*J, dm));
2643   PetscFunctionReturn(0);
2644 }
2645 
2646 /*@
2647   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2648 
2649   Not collective
2650 
2651   Input Parameter:
2652 . mesh - The DMPlex
2653 
2654   Output Parameters:
2655 . subsection - The subdomain section
2656 
2657   Level: developer
2658 
2659 .seealso:
2660 @*/
2661 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2662 {
2663   DM_Plex *mesh = (DM_Plex *)dm->data;
2664 
2665   PetscFunctionBegin;
2666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2667   if (!mesh->subdomainSection) {
2668     PetscSection section;
2669     PetscSF      sf;
2670 
2671     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2672     PetscCall(DMGetLocalSection(dm, &section));
2673     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2674     PetscCall(PetscSFDestroy(&sf));
2675   }
2676   *subsection = mesh->subdomainSection;
2677   PetscFunctionReturn(0);
2678 }
2679 
2680 /*@
2681   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2682 
2683   Not collective
2684 
2685   Input Parameter:
2686 . mesh - The DMPlex
2687 
2688   Output Parameters:
2689 + pStart - The first mesh point
2690 - pEnd   - The upper bound for mesh points
2691 
2692   Level: beginner
2693 
2694 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2695 @*/
2696 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2697 {
2698   DM_Plex *mesh = (DM_Plex *)dm->data;
2699 
2700   PetscFunctionBegin;
2701   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2702   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2703   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2704   PetscFunctionReturn(0);
2705 }
2706 
2707 /*@
2708   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2709 
2710   Not collective
2711 
2712   Input Parameters:
2713 + mesh - The DMPlex
2714 . pStart - The first mesh point
2715 - pEnd   - The upper bound for mesh points
2716 
2717   Output Parameters:
2718 
2719   Level: beginner
2720 
2721 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2722 @*/
2723 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2724 {
2725   DM_Plex *mesh = (DM_Plex *)dm->data;
2726 
2727   PetscFunctionBegin;
2728   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2729   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2730   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2731   PetscFunctionReturn(0);
2732 }
2733 
2734 /*@
2735   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2736 
2737   Not collective
2738 
2739   Input Parameters:
2740 + mesh - The DMPlex
2741 - p - The point, which must lie in the chart set with DMPlexSetChart()
2742 
2743   Output Parameter:
2744 . size - The cone size for point p
2745 
2746   Level: beginner
2747 
2748 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2749 @*/
2750 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2751 {
2752   DM_Plex *mesh = (DM_Plex *)dm->data;
2753 
2754   PetscFunctionBegin;
2755   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2756   PetscValidIntPointer(size, 3);
2757   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2758   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2759   PetscFunctionReturn(0);
2760 }
2761 
2762 /*@
2763   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2764 
2765   Not collective
2766 
2767   Input Parameters:
2768 + mesh - The DMPlex
2769 . p - The point, which must lie in the chart set with DMPlexSetChart()
2770 - size - The cone size for point p
2771 
2772   Output Parameter:
2773 
2774   Note:
2775   This should be called after DMPlexSetChart().
2776 
2777   Level: beginner
2778 
2779 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2780 @*/
2781 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2782 {
2783   DM_Plex *mesh = (DM_Plex *)dm->data;
2784 
2785   PetscFunctionBegin;
2786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2787   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2788   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2789   PetscFunctionReturn(0);
2790 }
2791 
2792 /*@C
2793   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2794 
2795   Not collective
2796 
2797   Input Parameters:
2798 + dm - The DMPlex
2799 - p - The point, which must lie in the chart set with DMPlexSetChart()
2800 
2801   Output Parameter:
2802 . cone - An array of points which are on the in-edges for point p
2803 
2804   Level: beginner
2805 
2806   Fortran Notes:
2807   Since it returns an array, this routine is only available in Fortran 90, and you must
2808   include petsc.h90 in your code.
2809   You must also call DMPlexRestoreCone() after you finish using the returned array.
2810   DMPlexRestoreCone() is not needed/available in C.
2811 
2812 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2813 @*/
2814 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2815 {
2816   DM_Plex *mesh = (DM_Plex *)dm->data;
2817   PetscInt off;
2818 
2819   PetscFunctionBegin;
2820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2821   PetscValidPointer(cone, 3);
2822   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2823   *cone = &mesh->cones[off];
2824   PetscFunctionReturn(0);
2825 }
2826 
2827 /*@C
2828   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2829 
2830   Not collective
2831 
2832   Input Parameters:
2833 + dm - The DMPlex
2834 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2835 
2836   Output Parameters:
2837 + pConesSection - PetscSection describing the layout of pCones
2838 - pCones - An array of points which are on the in-edges for the point set p
2839 
2840   Level: intermediate
2841 
2842 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2843 @*/
2844 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2845 {
2846   PetscSection cs, newcs;
2847   PetscInt    *cones;
2848   PetscInt    *newarr = NULL;
2849   PetscInt     n;
2850 
2851   PetscFunctionBegin;
2852   PetscCall(DMPlexGetCones(dm, &cones));
2853   PetscCall(DMPlexGetConeSection(dm, &cs));
2854   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2855   if (pConesSection) *pConesSection = newcs;
2856   if (pCones) {
2857     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2858     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2859   }
2860   PetscFunctionReturn(0);
2861 }
2862 
2863 /*@
2864   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2865 
2866   Not collective
2867 
2868   Input Parameters:
2869 + dm - The DMPlex
2870 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2871 
2872   Output Parameter:
2873 . expandedPoints - An array of vertices recursively expanded from input points
2874 
2875   Level: advanced
2876 
2877   Notes:
2878   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2879   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2880 
2881 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2882 @*/
2883 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2884 {
2885   IS      *expandedPointsAll;
2886   PetscInt depth;
2887 
2888   PetscFunctionBegin;
2889   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2890   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2891   PetscValidPointer(expandedPoints, 3);
2892   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2893   *expandedPoints = expandedPointsAll[0];
2894   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2895   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2896   PetscFunctionReturn(0);
2897 }
2898 
2899 /*@
2900   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).
2901 
2902   Not collective
2903 
2904   Input Parameters:
2905 + dm - The DMPlex
2906 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2907 
2908   Output Parameters:
2909 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2910 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2911 - sections - (optional) An array of sections which describe mappings from points to their cone points
2912 
2913   Level: advanced
2914 
2915   Notes:
2916   Like DMPlexGetConeTuple() but recursive.
2917 
2918   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.
2919   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2920 
2921   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:
2922   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2923   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2924 
2925 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2926 @*/
2927 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2928 {
2929   const PetscInt *arr0 = NULL, *cone = NULL;
2930   PetscInt       *arr = NULL, *newarr = NULL;
2931   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2932   IS             *expandedPoints_;
2933   PetscSection   *sections_;
2934 
2935   PetscFunctionBegin;
2936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2937   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2938   if (depth) PetscValidIntPointer(depth, 3);
2939   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2940   if (sections) PetscValidPointer(sections, 5);
2941   PetscCall(ISGetLocalSize(points, &n));
2942   PetscCall(ISGetIndices(points, &arr0));
2943   PetscCall(DMPlexGetDepth(dm, &depth_));
2944   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2945   PetscCall(PetscCalloc1(depth_, &sections_));
2946   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2947   for (d = depth_ - 1; d >= 0; d--) {
2948     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2949     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2950     for (i = 0; i < n; i++) {
2951       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2952       if (arr[i] >= start && arr[i] < end) {
2953         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2954         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2955       } else {
2956         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2957       }
2958     }
2959     PetscCall(PetscSectionSetUp(sections_[d]));
2960     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2961     PetscCall(PetscMalloc1(newn, &newarr));
2962     for (i = 0; i < n; i++) {
2963       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2964       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2965       if (cn > 1) {
2966         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2967         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2968       } else {
2969         newarr[co] = arr[i];
2970       }
2971     }
2972     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2973     arr = newarr;
2974     n   = newn;
2975   }
2976   PetscCall(ISRestoreIndices(points, &arr0));
2977   *depth = depth_;
2978   if (expandedPoints) *expandedPoints = expandedPoints_;
2979   else {
2980     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2981     PetscCall(PetscFree(expandedPoints_));
2982   }
2983   if (sections) *sections = sections_;
2984   else {
2985     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2986     PetscCall(PetscFree(sections_));
2987   }
2988   PetscFunctionReturn(0);
2989 }
2990 
2991 /*@
2992   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2993 
2994   Not collective
2995 
2996   Input Parameters:
2997 + dm - The DMPlex
2998 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2999 
3000   Output Parameters:
3001 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
3002 . expandedPoints - (optional) An array of recursively expanded cones
3003 - sections - (optional) An array of sections which describe mappings from points to their cone points
3004 
3005   Level: advanced
3006 
3007   Notes:
3008   See DMPlexGetConeRecursive() for details.
3009 
3010 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
3011 @*/
3012 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3013 {
3014   PetscInt d, depth_;
3015 
3016   PetscFunctionBegin;
3017   PetscCall(DMPlexGetDepth(dm, &depth_));
3018   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3019   if (depth) *depth = 0;
3020   if (expandedPoints) {
3021     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3022     PetscCall(PetscFree(*expandedPoints));
3023   }
3024   if (sections) {
3025     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3026     PetscCall(PetscFree(*sections));
3027   }
3028   PetscFunctionReturn(0);
3029 }
3030 
3031 /*@
3032   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
3033 
3034   Not collective
3035 
3036   Input Parameters:
3037 + mesh - The DMPlex
3038 . p - The point, which must lie in the chart set with DMPlexSetChart()
3039 - cone - An array of points which are on the in-edges for point p
3040 
3041   Output Parameter:
3042 
3043   Note:
3044   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3045 
3046   Level: beginner
3047 
3048 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3049 @*/
3050 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3051 {
3052   DM_Plex *mesh = (DM_Plex *)dm->data;
3053   PetscInt pStart, pEnd;
3054   PetscInt dof, off, c;
3055 
3056   PetscFunctionBegin;
3057   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3058   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3059   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3060   if (dof) PetscValidIntPointer(cone, 3);
3061   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3062   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);
3063   for (c = 0; c < dof; ++c) {
3064     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);
3065     mesh->cones[off + c] = cone[c];
3066   }
3067   PetscFunctionReturn(0);
3068 }
3069 
3070 /*@C
3071   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3072 
3073   Not collective
3074 
3075   Input Parameters:
3076 + mesh - The DMPlex
3077 - p - The point, which must lie in the chart set with DMPlexSetChart()
3078 
3079   Output Parameter:
3080 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3081                     integer giving the prescription for cone traversal.
3082 
3083   Level: beginner
3084 
3085   Notes:
3086   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3087   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3088   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3089   with the identity.
3090 
3091   Fortran Notes:
3092   Since it returns an array, this routine is only available in Fortran 90, and you must
3093   include petsc.h90 in your code.
3094   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3095   DMPlexRestoreConeOrientation() is not needed/available in C.
3096 
3097 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3098 @*/
3099 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3100 {
3101   DM_Plex *mesh = (DM_Plex *)dm->data;
3102   PetscInt off;
3103 
3104   PetscFunctionBegin;
3105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3106   if (PetscDefined(USE_DEBUG)) {
3107     PetscInt dof;
3108     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3109     if (dof) PetscValidPointer(coneOrientation, 3);
3110   }
3111   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3112 
3113   *coneOrientation = &mesh->coneOrientations[off];
3114   PetscFunctionReturn(0);
3115 }
3116 
3117 /*@
3118   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3119 
3120   Not collective
3121 
3122   Input Parameters:
3123 + mesh - The DMPlex
3124 . p - The point, which must lie in the chart set with DMPlexSetChart()
3125 - coneOrientation - An array of orientations
3126   Output Parameter:
3127 
3128   Notes:
3129   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3130 
3131   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3132 
3133   Level: beginner
3134 
3135 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3136 @*/
3137 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3138 {
3139   DM_Plex *mesh = (DM_Plex *)dm->data;
3140   PetscInt pStart, pEnd;
3141   PetscInt dof, off, c;
3142 
3143   PetscFunctionBegin;
3144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3145   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3146   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3147   if (dof) PetscValidIntPointer(coneOrientation, 3);
3148   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3149   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);
3150   for (c = 0; c < dof; ++c) {
3151     PetscInt cdof, o = coneOrientation[c];
3152 
3153     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3154     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);
3155     mesh->coneOrientations[off + c] = o;
3156   }
3157   PetscFunctionReturn(0);
3158 }
3159 
3160 /*@
3161   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3162 
3163   Not collective
3164 
3165   Input Parameters:
3166 + mesh - The DMPlex
3167 . p - The point, which must lie in the chart set with DMPlexSetChart()
3168 . conePos - The local index in the cone where the point should be put
3169 - conePoint - The mesh point to insert
3170 
3171   Level: beginner
3172 
3173 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3174 @*/
3175 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3176 {
3177   DM_Plex *mesh = (DM_Plex *)dm->data;
3178   PetscInt pStart, pEnd;
3179   PetscInt dof, off;
3180 
3181   PetscFunctionBegin;
3182   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3183   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3184   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);
3185   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);
3186   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3187   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3188   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);
3189   mesh->cones[off + conePos] = conePoint;
3190   PetscFunctionReturn(0);
3191 }
3192 
3193 /*@
3194   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3195 
3196   Not collective
3197 
3198   Input Parameters:
3199 + mesh - The DMPlex
3200 . p - The point, which must lie in the chart set with DMPlexSetChart()
3201 . conePos - The local index in the cone where the point should be put
3202 - coneOrientation - The point orientation to insert
3203 
3204   Level: beginner
3205 
3206   Notes:
3207   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3208 
3209 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3210 @*/
3211 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt pStart, pEnd;
3215   PetscInt dof, off;
3216 
3217   PetscFunctionBegin;
3218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3219   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3220   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);
3221   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3222   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3223   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);
3224   mesh->coneOrientations[off + conePos] = coneOrientation;
3225   PetscFunctionReturn(0);
3226 }
3227 
3228 /*@C
3229   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3230 
3231   Not collective
3232 
3233   Input Parameters:
3234 + dm - The DMPlex
3235 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3236 
3237   Output Parameters:
3238 + cone - An array of points which are on the in-edges for point p
3239 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3240         integer giving the prescription for cone traversal.
3241 
3242   Level: beginner
3243 
3244   Notes:
3245   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3246   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3247   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3248   with the identity.
3249 
3250   Fortran Notes:
3251   Since it returns an array, this routine is only available in Fortran 90, and you must
3252   include petsc.h90 in your code.
3253   You must also call DMPlexRestoreCone() after you finish using the returned array.
3254   DMPlexRestoreCone() is not needed/available in C.
3255 
3256 .seealso: `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3257 @*/
3258 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3259 {
3260   DM_Plex *mesh = (DM_Plex *)dm->data;
3261 
3262   PetscFunctionBegin;
3263   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3264   if (mesh->tr) {
3265     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3266   } else {
3267     PetscInt off;
3268     if (PetscDefined(USE_DEBUG)) {
3269       PetscInt dof;
3270       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3271       if (dof) {
3272         if (cone) PetscValidPointer(cone, 3);
3273         if (ornt) PetscValidPointer(ornt, 4);
3274       }
3275     }
3276     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3277     if (cone) *cone = &mesh->cones[off];
3278     if (ornt) *ornt = &mesh->coneOrientations[off];
3279   }
3280   PetscFunctionReturn(0);
3281 }
3282 
3283 /*@C
3284   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3285 
3286   Not collective
3287 
3288   Input Parameters:
3289 + dm - The DMPlex
3290 . p  - The point, which must lie in the chart set with DMPlexSetChart()
3291 . cone - An array of points which are on the in-edges for point p
3292 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3293         integer giving the prescription for cone traversal.
3294 
3295   Level: beginner
3296 
3297   Notes:
3298   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3299   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3300   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3301   with the identity.
3302 
3303   Fortran Notes:
3304   Since it returns an array, this routine is only available in Fortran 90, and you must
3305   include petsc.h90 in your code.
3306   You must also call DMPlexRestoreCone() after you finish using the returned array.
3307   DMPlexRestoreCone() is not needed/available in C.
3308 
3309 .seealso: `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3310 @*/
3311 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3312 {
3313   DM_Plex *mesh = (DM_Plex *)dm->data;
3314 
3315   PetscFunctionBegin;
3316   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3317   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3318   PetscFunctionReturn(0);
3319 }
3320 
3321 /*@
3322   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3323 
3324   Not collective
3325 
3326   Input Parameters:
3327 + mesh - The DMPlex
3328 - p - The point, which must lie in the chart set with DMPlexSetChart()
3329 
3330   Output Parameter:
3331 . size - The support size for point p
3332 
3333   Level: beginner
3334 
3335 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3336 @*/
3337 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3338 {
3339   DM_Plex *mesh = (DM_Plex *)dm->data;
3340 
3341   PetscFunctionBegin;
3342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3343   PetscValidIntPointer(size, 3);
3344   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3345   PetscFunctionReturn(0);
3346 }
3347 
3348 /*@
3349   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3350 
3351   Not collective
3352 
3353   Input Parameters:
3354 + mesh - The DMPlex
3355 . p - The point, which must lie in the chart set with DMPlexSetChart()
3356 - size - The support size for point p
3357 
3358   Output Parameter:
3359 
3360   Note:
3361   This should be called after DMPlexSetChart().
3362 
3363   Level: beginner
3364 
3365 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3366 @*/
3367 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3368 {
3369   DM_Plex *mesh = (DM_Plex *)dm->data;
3370 
3371   PetscFunctionBegin;
3372   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3373   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3374   PetscFunctionReturn(0);
3375 }
3376 
3377 /*@C
3378   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3379 
3380   Not collective
3381 
3382   Input Parameters:
3383 + mesh - The DMPlex
3384 - p - The point, which must lie in the chart set with DMPlexSetChart()
3385 
3386   Output Parameter:
3387 . support - An array of points which are on the out-edges for point p
3388 
3389   Level: beginner
3390 
3391   Fortran Notes:
3392   Since it returns an array, this routine is only available in Fortran 90, and you must
3393   include petsc.h90 in your code.
3394   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3395   DMPlexRestoreSupport() is not needed/available in C.
3396 
3397 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3398 @*/
3399 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3400 {
3401   DM_Plex *mesh = (DM_Plex *)dm->data;
3402   PetscInt off;
3403 
3404   PetscFunctionBegin;
3405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3406   PetscValidPointer(support, 3);
3407   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3408   *support = &mesh->supports[off];
3409   PetscFunctionReturn(0);
3410 }
3411 
3412 /*@
3413   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3414 
3415   Not collective
3416 
3417   Input Parameters:
3418 + mesh - The DMPlex
3419 . p - The point, which must lie in the chart set with DMPlexSetChart()
3420 - support - An array of points which are on the out-edges for point p
3421 
3422   Output Parameter:
3423 
3424   Note:
3425   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3426 
3427   Level: beginner
3428 
3429 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3430 @*/
3431 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3432 {
3433   DM_Plex *mesh = (DM_Plex *)dm->data;
3434   PetscInt pStart, pEnd;
3435   PetscInt dof, off, c;
3436 
3437   PetscFunctionBegin;
3438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3439   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3440   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3441   if (dof) PetscValidIntPointer(support, 3);
3442   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3443   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);
3444   for (c = 0; c < dof; ++c) {
3445     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);
3446     mesh->supports[off + c] = support[c];
3447   }
3448   PetscFunctionReturn(0);
3449 }
3450 
3451 /*@
3452   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3453 
3454   Not collective
3455 
3456   Input Parameters:
3457 + mesh - The DMPlex
3458 . p - The point, which must lie in the chart set with DMPlexSetChart()
3459 . supportPos - The local index in the cone where the point should be put
3460 - supportPoint - The mesh point to insert
3461 
3462   Level: beginner
3463 
3464 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3465 @*/
3466 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3467 {
3468   DM_Plex *mesh = (DM_Plex *)dm->data;
3469   PetscInt pStart, pEnd;
3470   PetscInt dof, off;
3471 
3472   PetscFunctionBegin;
3473   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3474   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3475   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3476   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3477   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);
3478   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);
3479   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);
3480   mesh->supports[off + supportPos] = supportPoint;
3481   PetscFunctionReturn(0);
3482 }
3483 
3484 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3485 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3486 {
3487   switch (ct) {
3488   case DM_POLYTOPE_SEGMENT:
3489     if (o == -1) return -2;
3490     break;
3491   case DM_POLYTOPE_TRIANGLE:
3492     if (o == -3) return -1;
3493     if (o == -2) return -3;
3494     if (o == -1) return -2;
3495     break;
3496   case DM_POLYTOPE_QUADRILATERAL:
3497     if (o == -4) return -2;
3498     if (o == -3) return -1;
3499     if (o == -2) return -4;
3500     if (o == -1) return -3;
3501     break;
3502   default:
3503     return o;
3504   }
3505   return o;
3506 }
3507 
3508 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3509 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3510 {
3511   switch (ct) {
3512   case DM_POLYTOPE_SEGMENT:
3513     if ((o == -2) || (o == 1)) return -1;
3514     if (o == -1) return 0;
3515     break;
3516   case DM_POLYTOPE_TRIANGLE:
3517     if (o == -3) return -2;
3518     if (o == -2) return -1;
3519     if (o == -1) return -3;
3520     break;
3521   case DM_POLYTOPE_QUADRILATERAL:
3522     if (o == -4) return -2;
3523     if (o == -3) return -1;
3524     if (o == -2) return -4;
3525     if (o == -1) return -3;
3526     break;
3527   default:
3528     return o;
3529   }
3530   return o;
3531 }
3532 
3533 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3534 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3535 {
3536   PetscInt pStart, pEnd, p;
3537 
3538   PetscFunctionBegin;
3539   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3540   for (p = pStart; p < pEnd; ++p) {
3541     const PetscInt *cone, *ornt;
3542     PetscInt        coneSize, c;
3543 
3544     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3545     PetscCall(DMPlexGetCone(dm, p, &cone));
3546     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3547     for (c = 0; c < coneSize; ++c) {
3548       DMPolytopeType ct;
3549       const PetscInt o = ornt[c];
3550 
3551       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3552       switch (ct) {
3553       case DM_POLYTOPE_SEGMENT:
3554         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3555         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3556         break;
3557       case DM_POLYTOPE_TRIANGLE:
3558         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3559         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3560         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3561         break;
3562       case DM_POLYTOPE_QUADRILATERAL:
3563         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3564         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3565         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3566         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3567         break;
3568       default:
3569         break;
3570       }
3571     }
3572   }
3573   PetscFunctionReturn(0);
3574 }
3575 
3576 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3577 {
3578   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3579   PetscInt       *closure;
3580   const PetscInt *tmp = NULL, *tmpO = NULL;
3581   PetscInt        off = 0, tmpSize, t;
3582 
3583   PetscFunctionBeginHot;
3584   if (ornt) {
3585     PetscCall(DMPlexGetCellType(dm, p, &ct));
3586     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3587   }
3588   if (*points) {
3589     closure = *points;
3590   } else {
3591     PetscInt maxConeSize, maxSupportSize;
3592     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3593     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3594   }
3595   if (useCone) {
3596     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3597     PetscCall(DMPlexGetCone(dm, p, &tmp));
3598     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3599   } else {
3600     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3601     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3602   }
3603   if (ct == DM_POLYTOPE_UNKNOWN) {
3604     closure[off++] = p;
3605     closure[off++] = 0;
3606     for (t = 0; t < tmpSize; ++t) {
3607       closure[off++] = tmp[t];
3608       closure[off++] = tmpO ? tmpO[t] : 0;
3609     }
3610   } else {
3611     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3612 
3613     /* We assume that cells with a valid type have faces with a valid type */
3614     closure[off++] = p;
3615     closure[off++] = ornt;
3616     for (t = 0; t < tmpSize; ++t) {
3617       DMPolytopeType ft;
3618 
3619       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3620       closure[off++] = tmp[arr[t]];
3621       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3622     }
3623   }
3624   if (numPoints) *numPoints = tmpSize + 1;
3625   if (points) *points = closure;
3626   PetscFunctionReturn(0);
3627 }
3628 
3629 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3630 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3631 {
3632   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3633   const PetscInt *cone, *ornt;
3634   PetscInt       *pts, *closure = NULL;
3635   DMPolytopeType  ft;
3636   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3637   PetscInt        dim, coneSize, c, d, clSize, cl;
3638 
3639   PetscFunctionBeginHot;
3640   PetscCall(DMGetDimension(dm, &dim));
3641   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3642   PetscCall(DMPlexGetCone(dm, point, &cone));
3643   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3644   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3645   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3646   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3647   maxSize       = PetscMax(coneSeries, supportSeries);
3648   if (*points) {
3649     pts = *points;
3650   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3651   c        = 0;
3652   pts[c++] = point;
3653   pts[c++] = o;
3654   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3655   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3656   for (cl = 0; cl < clSize * 2; cl += 2) {
3657     pts[c++] = closure[cl];
3658     pts[c++] = closure[cl + 1];
3659   }
3660   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3661   for (cl = 0; cl < clSize * 2; cl += 2) {
3662     pts[c++] = closure[cl];
3663     pts[c++] = closure[cl + 1];
3664   }
3665   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3666   for (d = 2; d < coneSize; ++d) {
3667     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3668     pts[c++] = cone[arr[d * 2 + 0]];
3669     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3670   }
3671   if (dim >= 3) {
3672     for (d = 2; d < coneSize; ++d) {
3673       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3674       const PetscInt *fcone, *fornt;
3675       PetscInt        fconeSize, fc, i;
3676 
3677       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3678       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3679       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3680       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3681       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3682       for (fc = 0; fc < fconeSize; ++fc) {
3683         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3684         const PetscInt co = farr[fc * 2 + 1];
3685 
3686         for (i = 0; i < c; i += 2)
3687           if (pts[i] == cp) break;
3688         if (i == c) {
3689           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3690           pts[c++] = cp;
3691           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3692         }
3693       }
3694     }
3695   }
3696   *numPoints = c / 2;
3697   *points    = pts;
3698   PetscFunctionReturn(0);
3699 }
3700 
3701 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3702 {
3703   DMPolytopeType ct;
3704   PetscInt      *closure, *fifo;
3705   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3706   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3707   PetscInt       depth, maxSize;
3708 
3709   PetscFunctionBeginHot;
3710   PetscCall(DMPlexGetDepth(dm, &depth));
3711   if (depth == 1) {
3712     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3713     PetscFunctionReturn(0);
3714   }
3715   PetscCall(DMPlexGetCellType(dm, p, &ct));
3716   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3717   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3718     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3719     PetscFunctionReturn(0);
3720   }
3721   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3722   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3723   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3724   maxSize       = PetscMax(coneSeries, supportSeries);
3725   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3726   if (*points) {
3727     closure = *points;
3728   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3729   closure[closureSize++] = p;
3730   closure[closureSize++] = ornt;
3731   fifo[fifoSize++]       = p;
3732   fifo[fifoSize++]       = ornt;
3733   fifo[fifoSize++]       = ct;
3734   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3735   while (fifoSize - fifoStart) {
3736     const PetscInt       q    = fifo[fifoStart++];
3737     const PetscInt       o    = fifo[fifoStart++];
3738     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3739     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3740     const PetscInt      *tmp, *tmpO;
3741     PetscInt             tmpSize, t;
3742 
3743     if (PetscDefined(USE_DEBUG)) {
3744       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3745       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);
3746     }
3747     if (useCone) {
3748       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3749       PetscCall(DMPlexGetCone(dm, q, &tmp));
3750       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3751     } else {
3752       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3753       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3754       tmpO = NULL;
3755     }
3756     for (t = 0; t < tmpSize; ++t) {
3757       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3758       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3759       const PetscInt cp = tmp[ip];
3760       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3761       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3762       PetscInt       c;
3763 
3764       /* Check for duplicate */
3765       for (c = 0; c < closureSize; c += 2) {
3766         if (closure[c] == cp) break;
3767       }
3768       if (c == closureSize) {
3769         closure[closureSize++] = cp;
3770         closure[closureSize++] = co;
3771         fifo[fifoSize++]       = cp;
3772         fifo[fifoSize++]       = co;
3773         fifo[fifoSize++]       = ct;
3774       }
3775     }
3776   }
3777   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3778   if (numPoints) *numPoints = closureSize / 2;
3779   if (points) *points = closure;
3780   PetscFunctionReturn(0);
3781 }
3782 
3783 /*@C
3784   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3785 
3786   Not collective
3787 
3788   Input Parameters:
3789 + dm      - The DMPlex
3790 . p       - The mesh point
3791 - useCone - PETSC_TRUE for the closure, otherwise return the star
3792 
3793   Input/Output Parameter:
3794 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3795            if NULL on input, internal storage will be returned, otherwise the provided array is used
3796 
3797   Output Parameter:
3798 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3799 
3800   Note:
3801   If using internal storage (points is NULL on input), each call overwrites the last output.
3802 
3803   Fortran Note:
3804   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3805 
3806   Level: beginner
3807 
3808 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3809 @*/
3810 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3811 {
3812   PetscFunctionBeginHot;
3813   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3814   if (numPoints) PetscValidIntPointer(numPoints, 4);
3815   if (points) PetscValidPointer(points, 5);
3816   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3817   PetscFunctionReturn(0);
3818 }
3819 
3820 /*@C
3821   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3822 
3823   Not collective
3824 
3825   Input Parameters:
3826 + dm        - The DMPlex
3827 . p         - The mesh point
3828 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3829 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3830 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3831 
3832   Note:
3833   If not using internal storage (points is not NULL on input), this call is unnecessary
3834 
3835   Level: beginner
3836 
3837 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3838 @*/
3839 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3840 {
3841   PetscFunctionBeginHot;
3842   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3843   if (numPoints) *numPoints = 0;
3844   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3845   PetscFunctionReturn(0);
3846 }
3847 
3848 /*@
3849   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3850 
3851   Not collective
3852 
3853   Input Parameter:
3854 . mesh - The DMPlex
3855 
3856   Output Parameters:
3857 + maxConeSize - The maximum number of in-edges
3858 - maxSupportSize - The maximum number of out-edges
3859 
3860   Level: beginner
3861 
3862 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3863 @*/
3864 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3865 {
3866   DM_Plex *mesh = (DM_Plex *)dm->data;
3867 
3868   PetscFunctionBegin;
3869   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3870   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3871   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3872   PetscFunctionReturn(0);
3873 }
3874 
3875 PetscErrorCode DMSetUp_Plex(DM dm)
3876 {
3877   DM_Plex *mesh = (DM_Plex *)dm->data;
3878   PetscInt size, maxSupportSize;
3879 
3880   PetscFunctionBegin;
3881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3882   PetscCall(PetscSectionSetUp(mesh->coneSection));
3883   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3884   PetscCall(PetscMalloc1(size, &mesh->cones));
3885   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3886   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3887   if (maxSupportSize) {
3888     PetscCall(PetscSectionSetUp(mesh->supportSection));
3889     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3890     PetscCall(PetscMalloc1(size, &mesh->supports));
3891   }
3892   PetscFunctionReturn(0);
3893 }
3894 
3895 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3896 {
3897   PetscFunctionBegin;
3898   if (subdm) PetscCall(DMClone(dm, subdm));
3899   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3900   if (subdm) (*subdm)->useNatural = dm->useNatural;
3901   if (dm->useNatural && dm->sfMigration) {
3902     PetscSF sfNatural;
3903 
3904     (*subdm)->sfMigration = dm->sfMigration;
3905     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3906     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3907     (*subdm)->sfNatural = sfNatural;
3908   }
3909   PetscFunctionReturn(0);
3910 }
3911 
3912 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3913 {
3914   PetscInt i = 0;
3915 
3916   PetscFunctionBegin;
3917   PetscCall(DMClone(dms[0], superdm));
3918   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3919   (*superdm)->useNatural = PETSC_FALSE;
3920   for (i = 0; i < len; i++) {
3921     if (dms[i]->useNatural && dms[i]->sfMigration) {
3922       PetscSF sfNatural;
3923 
3924       (*superdm)->sfMigration = dms[i]->sfMigration;
3925       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3926       (*superdm)->useNatural = PETSC_TRUE;
3927       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3928       (*superdm)->sfNatural = sfNatural;
3929       break;
3930     }
3931   }
3932   PetscFunctionReturn(0);
3933 }
3934 
3935 /*@
3936   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3937 
3938   Not collective
3939 
3940   Input Parameter:
3941 . mesh - The DMPlex
3942 
3943   Output Parameter:
3944 
3945   Note:
3946   This should be called after all calls to DMPlexSetCone()
3947 
3948   Level: beginner
3949 
3950 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3951 @*/
3952 PetscErrorCode DMPlexSymmetrize(DM dm)
3953 {
3954   DM_Plex  *mesh = (DM_Plex *)dm->data;
3955   PetscInt *offsets;
3956   PetscInt  supportSize;
3957   PetscInt  pStart, pEnd, p;
3958 
3959   PetscFunctionBegin;
3960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3961   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3962   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3963   /* Calculate support sizes */
3964   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3965   for (p = pStart; p < pEnd; ++p) {
3966     PetscInt dof, off, c;
3967 
3968     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3969     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3970     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3971   }
3972   PetscCall(PetscSectionSetUp(mesh->supportSection));
3973   /* Calculate supports */
3974   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3975   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3976   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3977   for (p = pStart; p < pEnd; ++p) {
3978     PetscInt dof, off, c;
3979 
3980     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3981     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3982     for (c = off; c < off + dof; ++c) {
3983       const PetscInt q = mesh->cones[c];
3984       PetscInt       offS;
3985 
3986       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3987 
3988       mesh->supports[offS + offsets[q]] = p;
3989       ++offsets[q];
3990     }
3991   }
3992   PetscCall(PetscFree(offsets));
3993   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3994   PetscFunctionReturn(0);
3995 }
3996 
3997 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3998 {
3999   IS stratumIS;
4000 
4001   PetscFunctionBegin;
4002   if (pStart >= pEnd) PetscFunctionReturn(0);
4003   if (PetscDefined(USE_DEBUG)) {
4004     PetscInt  qStart, qEnd, numLevels, level;
4005     PetscBool overlap = PETSC_FALSE;
4006     PetscCall(DMLabelGetNumValues(label, &numLevels));
4007     for (level = 0; level < numLevels; level++) {
4008       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4009       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4010         overlap = PETSC_TRUE;
4011         break;
4012       }
4013     }
4014     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);
4015   }
4016   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4017   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4018   PetscCall(ISDestroy(&stratumIS));
4019   PetscFunctionReturn(0);
4020 }
4021 
4022 /*@
4023   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4024   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4025   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4026   the DAG.
4027 
4028   Collective on dm
4029 
4030   Input Parameter:
4031 . mesh - The DMPlex
4032 
4033   Output Parameter:
4034 
4035   Notes:
4036   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4037   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4038   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
4039   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4040   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
4041 
4042   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4043   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4044   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
4045   to interpolate only that one (e0), so that
4046 $  cone(c0) = {e0, v2}
4047 $  cone(e0) = {v0, v1}
4048   If DMPlexStratify() is run on this mesh, it will give depths
4049 $  depth 0 = {v0, v1, v2}
4050 $  depth 1 = {e0, c0}
4051   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4052 
4053   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
4054 
4055   Level: beginner
4056 
4057 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4058 @*/
4059 PetscErrorCode DMPlexStratify(DM dm)
4060 {
4061   DM_Plex *mesh = (DM_Plex *)dm->data;
4062   DMLabel  label;
4063   PetscInt pStart, pEnd, p;
4064   PetscInt numRoots = 0, numLeaves = 0;
4065 
4066   PetscFunctionBegin;
4067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4068   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4069 
4070   /* Create depth label */
4071   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4072   PetscCall(DMCreateLabel(dm, "depth"));
4073   PetscCall(DMPlexGetDepthLabel(dm, &label));
4074 
4075   {
4076     /* Initialize roots and count leaves */
4077     PetscInt sMin = PETSC_MAX_INT;
4078     PetscInt sMax = PETSC_MIN_INT;
4079     PetscInt coneSize, supportSize;
4080 
4081     for (p = pStart; p < pEnd; ++p) {
4082       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4083       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4084       if (!coneSize && supportSize) {
4085         sMin = PetscMin(p, sMin);
4086         sMax = PetscMax(p, sMax);
4087         ++numRoots;
4088       } else if (!supportSize && coneSize) {
4089         ++numLeaves;
4090       } else if (!supportSize && !coneSize) {
4091         /* Isolated points */
4092         sMin = PetscMin(p, sMin);
4093         sMax = PetscMax(p, sMax);
4094       }
4095     }
4096     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4097   }
4098 
4099   if (numRoots + numLeaves == (pEnd - pStart)) {
4100     PetscInt sMin = PETSC_MAX_INT;
4101     PetscInt sMax = PETSC_MIN_INT;
4102     PetscInt coneSize, supportSize;
4103 
4104     for (p = pStart; p < pEnd; ++p) {
4105       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4106       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4107       if (!supportSize && coneSize) {
4108         sMin = PetscMin(p, sMin);
4109         sMax = PetscMax(p, sMax);
4110       }
4111     }
4112     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4113   } else {
4114     PetscInt level = 0;
4115     PetscInt qStart, qEnd, q;
4116 
4117     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4118     while (qEnd > qStart) {
4119       PetscInt sMin = PETSC_MAX_INT;
4120       PetscInt sMax = PETSC_MIN_INT;
4121 
4122       for (q = qStart; q < qEnd; ++q) {
4123         const PetscInt *support;
4124         PetscInt        supportSize, s;
4125 
4126         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4127         PetscCall(DMPlexGetSupport(dm, q, &support));
4128         for (s = 0; s < supportSize; ++s) {
4129           sMin = PetscMin(support[s], sMin);
4130           sMax = PetscMax(support[s], sMax);
4131         }
4132       }
4133       PetscCall(DMLabelGetNumValues(label, &level));
4134       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4135       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4136     }
4137   }
4138   { /* just in case there is an empty process */
4139     PetscInt numValues, maxValues = 0, v;
4140 
4141     PetscCall(DMLabelGetNumValues(label, &numValues));
4142     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4143     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4144   }
4145   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4146   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4147   PetscFunctionReturn(0);
4148 }
4149 
4150 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4151 {
4152   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4153   PetscInt       dim, depth, pheight, coneSize;
4154 
4155   PetscFunctionBeginHot;
4156   PetscCall(DMGetDimension(dm, &dim));
4157   PetscCall(DMPlexGetDepth(dm, &depth));
4158   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4159   pheight = depth - pdepth;
4160   if (depth <= 1) {
4161     switch (pdepth) {
4162     case 0:
4163       ct = DM_POLYTOPE_POINT;
4164       break;
4165     case 1:
4166       switch (coneSize) {
4167       case 2:
4168         ct = DM_POLYTOPE_SEGMENT;
4169         break;
4170       case 3:
4171         ct = DM_POLYTOPE_TRIANGLE;
4172         break;
4173       case 4:
4174         switch (dim) {
4175         case 2:
4176           ct = DM_POLYTOPE_QUADRILATERAL;
4177           break;
4178         case 3:
4179           ct = DM_POLYTOPE_TETRAHEDRON;
4180           break;
4181         default:
4182           break;
4183         }
4184         break;
4185       case 5:
4186         ct = DM_POLYTOPE_PYRAMID;
4187         break;
4188       case 6:
4189         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4190         break;
4191       case 8:
4192         ct = DM_POLYTOPE_HEXAHEDRON;
4193         break;
4194       default:
4195         break;
4196       }
4197     }
4198   } else {
4199     if (pdepth == 0) {
4200       ct = DM_POLYTOPE_POINT;
4201     } else if (pheight == 0) {
4202       switch (dim) {
4203       case 1:
4204         switch (coneSize) {
4205         case 2:
4206           ct = DM_POLYTOPE_SEGMENT;
4207           break;
4208         default:
4209           break;
4210         }
4211         break;
4212       case 2:
4213         switch (coneSize) {
4214         case 3:
4215           ct = DM_POLYTOPE_TRIANGLE;
4216           break;
4217         case 4:
4218           ct = DM_POLYTOPE_QUADRILATERAL;
4219           break;
4220         default:
4221           break;
4222         }
4223         break;
4224       case 3:
4225         switch (coneSize) {
4226         case 4:
4227           ct = DM_POLYTOPE_TETRAHEDRON;
4228           break;
4229         case 5: {
4230           const PetscInt *cone;
4231           PetscInt        faceConeSize;
4232 
4233           PetscCall(DMPlexGetCone(dm, p, &cone));
4234           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4235           switch (faceConeSize) {
4236           case 3:
4237             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4238             break;
4239           case 4:
4240             ct = DM_POLYTOPE_PYRAMID;
4241             break;
4242           }
4243         } break;
4244         case 6:
4245           ct = DM_POLYTOPE_HEXAHEDRON;
4246           break;
4247         default:
4248           break;
4249         }
4250         break;
4251       default:
4252         break;
4253       }
4254     } else if (pheight > 0) {
4255       switch (coneSize) {
4256       case 2:
4257         ct = DM_POLYTOPE_SEGMENT;
4258         break;
4259       case 3:
4260         ct = DM_POLYTOPE_TRIANGLE;
4261         break;
4262       case 4:
4263         ct = DM_POLYTOPE_QUADRILATERAL;
4264         break;
4265       default:
4266         break;
4267       }
4268     }
4269   }
4270   *pt = ct;
4271   PetscFunctionReturn(0);
4272 }
4273 
4274 /*@
4275   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4276 
4277   Collective on dm
4278 
4279   Input Parameter:
4280 . mesh - The DMPlex
4281 
4282   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4283 
4284   Level: developer
4285 
4286   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4287   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4288   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4289 
4290 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4291 @*/
4292 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4293 {
4294   DM_Plex *mesh;
4295   DMLabel  ctLabel;
4296   PetscInt pStart, pEnd, p;
4297 
4298   PetscFunctionBegin;
4299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4300   mesh = (DM_Plex *)dm->data;
4301   PetscCall(DMCreateLabel(dm, "celltype"));
4302   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4303   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4304   for (p = pStart; p < pEnd; ++p) {
4305     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4306     PetscInt       pdepth;
4307 
4308     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4309     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4310     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4311     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4312   }
4313   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4314   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4315   PetscFunctionReturn(0);
4316 }
4317 
4318 /*@C
4319   DMPlexGetJoin - Get an array for the join of the set of points
4320 
4321   Not Collective
4322 
4323   Input Parameters:
4324 + dm - The DMPlex object
4325 . numPoints - The number of input points for the join
4326 - points - The input points
4327 
4328   Output Parameters:
4329 + numCoveredPoints - The number of points in the join
4330 - coveredPoints - The points in the join
4331 
4332   Level: intermediate
4333 
4334   Note: Currently, this is restricted to a single level join
4335 
4336   Fortran Notes:
4337   Since it returns an array, this routine is only available in Fortran 90, and you must
4338   include petsc.h90 in your code.
4339 
4340   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4341 
4342 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4343 @*/
4344 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4345 {
4346   DM_Plex  *mesh = (DM_Plex *)dm->data;
4347   PetscInt *join[2];
4348   PetscInt  joinSize, i = 0;
4349   PetscInt  dof, off, p, c, m;
4350   PetscInt  maxSupportSize;
4351 
4352   PetscFunctionBegin;
4353   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4354   PetscValidIntPointer(points, 3);
4355   PetscValidIntPointer(numCoveredPoints, 4);
4356   PetscValidPointer(coveredPoints, 5);
4357   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4358   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4359   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4360   /* Copy in support of first point */
4361   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4362   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4363   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4364   /* Check each successive support */
4365   for (p = 1; p < numPoints; ++p) {
4366     PetscInt newJoinSize = 0;
4367 
4368     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4369     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4370     for (c = 0; c < dof; ++c) {
4371       const PetscInt point = mesh->supports[off + c];
4372 
4373       for (m = 0; m < joinSize; ++m) {
4374         if (point == join[i][m]) {
4375           join[1 - i][newJoinSize++] = point;
4376           break;
4377         }
4378       }
4379     }
4380     joinSize = newJoinSize;
4381     i        = 1 - i;
4382   }
4383   *numCoveredPoints = joinSize;
4384   *coveredPoints    = join[i];
4385   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4386   PetscFunctionReturn(0);
4387 }
4388 
4389 /*@C
4390   DMPlexRestoreJoin - Restore an array for the join of the set of points
4391 
4392   Not Collective
4393 
4394   Input Parameters:
4395 + dm - The DMPlex object
4396 . numPoints - The number of input points for the join
4397 - points - The input points
4398 
4399   Output Parameters:
4400 + numCoveredPoints - The number of points in the join
4401 - coveredPoints - The points in the join
4402 
4403   Fortran Notes:
4404   Since it returns an array, this routine is only available in Fortran 90, and you must
4405   include petsc.h90 in your code.
4406 
4407   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4408 
4409   Level: intermediate
4410 
4411 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4412 @*/
4413 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4414 {
4415   PetscFunctionBegin;
4416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4417   if (points) PetscValidIntPointer(points, 3);
4418   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4419   PetscValidPointer(coveredPoints, 5);
4420   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4421   if (numCoveredPoints) *numCoveredPoints = 0;
4422   PetscFunctionReturn(0);
4423 }
4424 
4425 /*@C
4426   DMPlexGetFullJoin - Get an array for the join of the set of points
4427 
4428   Not Collective
4429 
4430   Input Parameters:
4431 + dm - The DMPlex object
4432 . numPoints - The number of input points for the join
4433 - points - The input points
4434 
4435   Output Parameters:
4436 + numCoveredPoints - The number of points in the join
4437 - coveredPoints - The points in the join
4438 
4439   Fortran Notes:
4440   Since it returns an array, this routine is only available in Fortran 90, and you must
4441   include petsc.h90 in your code.
4442 
4443   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4444 
4445   Level: intermediate
4446 
4447 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4448 @*/
4449 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4450 {
4451   PetscInt *offsets, **closures;
4452   PetscInt *join[2];
4453   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4454   PetscInt  p, d, c, m, ms;
4455 
4456   PetscFunctionBegin;
4457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4458   PetscValidIntPointer(points, 3);
4459   PetscValidIntPointer(numCoveredPoints, 4);
4460   PetscValidPointer(coveredPoints, 5);
4461 
4462   PetscCall(DMPlexGetDepth(dm, &depth));
4463   PetscCall(PetscCalloc1(numPoints, &closures));
4464   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4465   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4466   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4467   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4468   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4469 
4470   for (p = 0; p < numPoints; ++p) {
4471     PetscInt closureSize;
4472 
4473     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4474 
4475     offsets[p * (depth + 2) + 0] = 0;
4476     for (d = 0; d < depth + 1; ++d) {
4477       PetscInt pStart, pEnd, i;
4478 
4479       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4480       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4481         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4482           offsets[p * (depth + 2) + d + 1] = i;
4483           break;
4484         }
4485       }
4486       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4487     }
4488     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);
4489   }
4490   for (d = 0; d < depth + 1; ++d) {
4491     PetscInt dof;
4492 
4493     /* Copy in support of first point */
4494     dof = offsets[d + 1] - offsets[d];
4495     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4496     /* Check each successive cone */
4497     for (p = 1; p < numPoints && joinSize; ++p) {
4498       PetscInt newJoinSize = 0;
4499 
4500       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4501       for (c = 0; c < dof; ++c) {
4502         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4503 
4504         for (m = 0; m < joinSize; ++m) {
4505           if (point == join[i][m]) {
4506             join[1 - i][newJoinSize++] = point;
4507             break;
4508           }
4509         }
4510       }
4511       joinSize = newJoinSize;
4512       i        = 1 - i;
4513     }
4514     if (joinSize) break;
4515   }
4516   *numCoveredPoints = joinSize;
4517   *coveredPoints    = join[i];
4518   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4519   PetscCall(PetscFree(closures));
4520   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4521   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4522   PetscFunctionReturn(0);
4523 }
4524 
4525 /*@C
4526   DMPlexGetMeet - Get an array for the meet of the set of points
4527 
4528   Not Collective
4529 
4530   Input Parameters:
4531 + dm - The DMPlex object
4532 . numPoints - The number of input points for the meet
4533 - points - The input points
4534 
4535   Output Parameters:
4536 + numCoveredPoints - The number of points in the meet
4537 - coveredPoints - The points in the meet
4538 
4539   Level: intermediate
4540 
4541   Note: Currently, this is restricted to a single level meet
4542 
4543   Fortran Notes:
4544   Since it returns an array, this routine is only available in Fortran 90, and you must
4545   include petsc.h90 in your code.
4546 
4547   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4548 
4549 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4550 @*/
4551 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4552 {
4553   DM_Plex  *mesh = (DM_Plex *)dm->data;
4554   PetscInt *meet[2];
4555   PetscInt  meetSize, i = 0;
4556   PetscInt  dof, off, p, c, m;
4557   PetscInt  maxConeSize;
4558 
4559   PetscFunctionBegin;
4560   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4561   PetscValidIntPointer(points, 3);
4562   PetscValidIntPointer(numCoveringPoints, 4);
4563   PetscValidPointer(coveringPoints, 5);
4564   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4565   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4566   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4567   /* Copy in cone of first point */
4568   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4569   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4570   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4571   /* Check each successive cone */
4572   for (p = 1; p < numPoints; ++p) {
4573     PetscInt newMeetSize = 0;
4574 
4575     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4576     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4577     for (c = 0; c < dof; ++c) {
4578       const PetscInt point = mesh->cones[off + c];
4579 
4580       for (m = 0; m < meetSize; ++m) {
4581         if (point == meet[i][m]) {
4582           meet[1 - i][newMeetSize++] = point;
4583           break;
4584         }
4585       }
4586     }
4587     meetSize = newMeetSize;
4588     i        = 1 - i;
4589   }
4590   *numCoveringPoints = meetSize;
4591   *coveringPoints    = meet[i];
4592   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4593   PetscFunctionReturn(0);
4594 }
4595 
4596 /*@C
4597   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4598 
4599   Not Collective
4600 
4601   Input Parameters:
4602 + dm - The DMPlex object
4603 . numPoints - The number of input points for the meet
4604 - points - The input points
4605 
4606   Output Parameters:
4607 + numCoveredPoints - The number of points in the meet
4608 - coveredPoints - The points in the meet
4609 
4610   Level: intermediate
4611 
4612   Fortran Notes:
4613   Since it returns an array, this routine is only available in Fortran 90, and you must
4614   include petsc.h90 in your code.
4615 
4616   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4617 
4618 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4619 @*/
4620 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4621 {
4622   PetscFunctionBegin;
4623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4624   if (points) PetscValidIntPointer(points, 3);
4625   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4626   PetscValidPointer(coveredPoints, 5);
4627   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4628   if (numCoveredPoints) *numCoveredPoints = 0;
4629   PetscFunctionReturn(0);
4630 }
4631 
4632 /*@C
4633   DMPlexGetFullMeet - Get an array for the meet of the set of points
4634 
4635   Not Collective
4636 
4637   Input Parameters:
4638 + dm - The DMPlex object
4639 . numPoints - The number of input points for the meet
4640 - points - The input points
4641 
4642   Output Parameters:
4643 + numCoveredPoints - The number of points in the meet
4644 - coveredPoints - The points in the meet
4645 
4646   Level: intermediate
4647 
4648   Fortran Notes:
4649   Since it returns an array, this routine is only available in Fortran 90, and you must
4650   include petsc.h90 in your code.
4651 
4652   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4653 
4654 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4655 @*/
4656 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4657 {
4658   PetscInt *offsets, **closures;
4659   PetscInt *meet[2];
4660   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4661   PetscInt  p, h, c, m, mc;
4662 
4663   PetscFunctionBegin;
4664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4665   PetscValidIntPointer(points, 3);
4666   PetscValidIntPointer(numCoveredPoints, 4);
4667   PetscValidPointer(coveredPoints, 5);
4668 
4669   PetscCall(DMPlexGetDepth(dm, &height));
4670   PetscCall(PetscMalloc1(numPoints, &closures));
4671   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4672   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4673   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4674   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4675   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4676 
4677   for (p = 0; p < numPoints; ++p) {
4678     PetscInt closureSize;
4679 
4680     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4681 
4682     offsets[p * (height + 2) + 0] = 0;
4683     for (h = 0; h < height + 1; ++h) {
4684       PetscInt pStart, pEnd, i;
4685 
4686       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4687       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4688         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4689           offsets[p * (height + 2) + h + 1] = i;
4690           break;
4691         }
4692       }
4693       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4694     }
4695     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);
4696   }
4697   for (h = 0; h < height + 1; ++h) {
4698     PetscInt dof;
4699 
4700     /* Copy in cone of first point */
4701     dof = offsets[h + 1] - offsets[h];
4702     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4703     /* Check each successive cone */
4704     for (p = 1; p < numPoints && meetSize; ++p) {
4705       PetscInt newMeetSize = 0;
4706 
4707       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4708       for (c = 0; c < dof; ++c) {
4709         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4710 
4711         for (m = 0; m < meetSize; ++m) {
4712           if (point == meet[i][m]) {
4713             meet[1 - i][newMeetSize++] = point;
4714             break;
4715           }
4716         }
4717       }
4718       meetSize = newMeetSize;
4719       i        = 1 - i;
4720     }
4721     if (meetSize) break;
4722   }
4723   *numCoveredPoints = meetSize;
4724   *coveredPoints    = meet[i];
4725   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4726   PetscCall(PetscFree(closures));
4727   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4728   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4729   PetscFunctionReturn(0);
4730 }
4731 
4732 /*@C
4733   DMPlexEqual - Determine if two DMs have the same topology
4734 
4735   Not Collective
4736 
4737   Input Parameters:
4738 + dmA - A DMPlex object
4739 - dmB - A DMPlex object
4740 
4741   Output Parameters:
4742 . equal - PETSC_TRUE if the topologies are identical
4743 
4744   Level: intermediate
4745 
4746   Notes:
4747   We are not solving graph isomorphism, so we do not permutation.
4748 
4749 .seealso: `DMPlexGetCone()`
4750 @*/
4751 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4752 {
4753   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4754 
4755   PetscFunctionBegin;
4756   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4757   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4758   PetscValidBoolPointer(equal, 3);
4759 
4760   *equal = PETSC_FALSE;
4761   PetscCall(DMPlexGetDepth(dmA, &depth));
4762   PetscCall(DMPlexGetDepth(dmB, &depthB));
4763   if (depth != depthB) PetscFunctionReturn(0);
4764   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4765   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4766   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4767   for (p = pStart; p < pEnd; ++p) {
4768     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4769     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4770 
4771     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4772     PetscCall(DMPlexGetCone(dmA, p, &cone));
4773     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4774     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4775     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4776     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4777     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4778     for (c = 0; c < coneSize; ++c) {
4779       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4780       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4781     }
4782     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4783     PetscCall(DMPlexGetSupport(dmA, p, &support));
4784     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4785     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4786     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4787     for (s = 0; s < supportSize; ++s) {
4788       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4789     }
4790   }
4791   *equal = PETSC_TRUE;
4792   PetscFunctionReturn(0);
4793 }
4794 
4795 /*@C
4796   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4797 
4798   Not Collective
4799 
4800   Input Parameters:
4801 + dm         - The DMPlex
4802 . cellDim    - The cell dimension
4803 - numCorners - The number of vertices on a cell
4804 
4805   Output Parameters:
4806 . numFaceVertices - The number of vertices on a face
4807 
4808   Level: developer
4809 
4810   Notes:
4811   Of course this can only work for a restricted set of symmetric shapes
4812 
4813 .seealso: `DMPlexGetCone()`
4814 @*/
4815 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4816 {
4817   MPI_Comm comm;
4818 
4819   PetscFunctionBegin;
4820   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4821   PetscValidIntPointer(numFaceVertices, 4);
4822   switch (cellDim) {
4823   case 0:
4824     *numFaceVertices = 0;
4825     break;
4826   case 1:
4827     *numFaceVertices = 1;
4828     break;
4829   case 2:
4830     switch (numCorners) {
4831     case 3:                 /* triangle */
4832       *numFaceVertices = 2; /* Edge has 2 vertices */
4833       break;
4834     case 4:                 /* quadrilateral */
4835       *numFaceVertices = 2; /* Edge has 2 vertices */
4836       break;
4837     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4838       *numFaceVertices = 3; /* Edge has 3 vertices */
4839       break;
4840     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4841       *numFaceVertices = 3; /* Edge has 3 vertices */
4842       break;
4843     default:
4844       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4845     }
4846     break;
4847   case 3:
4848     switch (numCorners) {
4849     case 4:                 /* tetradehdron */
4850       *numFaceVertices = 3; /* Face has 3 vertices */
4851       break;
4852     case 6:                 /* tet cohesive cells */
4853       *numFaceVertices = 4; /* Face has 4 vertices */
4854       break;
4855     case 8:                 /* hexahedron */
4856       *numFaceVertices = 4; /* Face has 4 vertices */
4857       break;
4858     case 9:                 /* tet cohesive Lagrange cells */
4859       *numFaceVertices = 6; /* Face has 6 vertices */
4860       break;
4861     case 10:                /* quadratic tetrahedron */
4862       *numFaceVertices = 6; /* Face has 6 vertices */
4863       break;
4864     case 12:                /* hex cohesive Lagrange cells */
4865       *numFaceVertices = 6; /* Face has 6 vertices */
4866       break;
4867     case 18:                /* quadratic tet cohesive Lagrange cells */
4868       *numFaceVertices = 6; /* Face has 6 vertices */
4869       break;
4870     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4871       *numFaceVertices = 9; /* Face has 9 vertices */
4872       break;
4873     default:
4874       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4875     }
4876     break;
4877   default:
4878     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4879   }
4880   PetscFunctionReturn(0);
4881 }
4882 
4883 /*@
4884   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4885 
4886   Not Collective
4887 
4888   Input Parameter:
4889 . dm    - The DMPlex object
4890 
4891   Output Parameter:
4892 . depthLabel - The DMLabel recording point depth
4893 
4894   Level: developer
4895 
4896 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4897 @*/
4898 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4899 {
4900   PetscFunctionBegin;
4901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4902   PetscValidPointer(depthLabel, 2);
4903   *depthLabel = dm->depthLabel;
4904   PetscFunctionReturn(0);
4905 }
4906 
4907 /*@
4908   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4909 
4910   Not Collective
4911 
4912   Input Parameter:
4913 . dm    - The DMPlex object
4914 
4915   Output Parameter:
4916 . depth - The number of strata (breadth first levels) in the DAG
4917 
4918   Level: developer
4919 
4920   Notes:
4921   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4922   The point depth is described more in detail in DMPlexGetDepthStratum().
4923   An empty mesh gives -1.
4924 
4925 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4926 @*/
4927 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4928 {
4929   DM_Plex *mesh = (DM_Plex *)dm->data;
4930   DMLabel  label;
4931   PetscInt d = 0;
4932 
4933   PetscFunctionBegin;
4934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4935   PetscValidIntPointer(depth, 2);
4936   if (mesh->tr) {
4937     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4938   } else {
4939     PetscCall(DMPlexGetDepthLabel(dm, &label));
4940     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4941     *depth = d - 1;
4942   }
4943   PetscFunctionReturn(0);
4944 }
4945 
4946 /*@
4947   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4948 
4949   Not Collective
4950 
4951   Input Parameters:
4952 + dm    - The DMPlex object
4953 - depth - The requested depth
4954 
4955   Output Parameters:
4956 + start - The first point at this depth
4957 - end   - One beyond the last point at this depth
4958 
4959   Notes:
4960   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4961   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4962   higher dimension, e.g., "edges".
4963 
4964   Level: developer
4965 
4966 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4967 @*/
4968 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4969 {
4970   DM_Plex *mesh = (DM_Plex *)dm->data;
4971   DMLabel  label;
4972   PetscInt pStart, pEnd;
4973 
4974   PetscFunctionBegin;
4975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4976   if (start) {
4977     PetscValidIntPointer(start, 3);
4978     *start = 0;
4979   }
4980   if (end) {
4981     PetscValidIntPointer(end, 4);
4982     *end = 0;
4983   }
4984   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4985   if (pStart == pEnd) PetscFunctionReturn(0);
4986   if (depth < 0) {
4987     if (start) *start = pStart;
4988     if (end) *end = pEnd;
4989     PetscFunctionReturn(0);
4990   }
4991   if (mesh->tr) {
4992     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
4993   } else {
4994     PetscCall(DMPlexGetDepthLabel(dm, &label));
4995     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4996     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4997   }
4998   PetscFunctionReturn(0);
4999 }
5000 
5001 /*@
5002   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
5003 
5004   Not Collective
5005 
5006   Input Parameters:
5007 + dm     - The DMPlex object
5008 - height - The requested height
5009 
5010   Output Parameters:
5011 + start - The first point at this height
5012 - end   - One beyond the last point at this height
5013 
5014   Notes:
5015   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5016   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
5017   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5018 
5019   Level: developer
5020 
5021 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5022 @*/
5023 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5024 {
5025   DMLabel  label;
5026   PetscInt depth, pStart, pEnd;
5027 
5028   PetscFunctionBegin;
5029   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5030   if (start) {
5031     PetscValidIntPointer(start, 3);
5032     *start = 0;
5033   }
5034   if (end) {
5035     PetscValidIntPointer(end, 4);
5036     *end = 0;
5037   }
5038   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5039   if (pStart == pEnd) PetscFunctionReturn(0);
5040   if (height < 0) {
5041     if (start) *start = pStart;
5042     if (end) *end = pEnd;
5043     PetscFunctionReturn(0);
5044   }
5045   PetscCall(DMPlexGetDepthLabel(dm, &label));
5046   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5047   PetscCall(DMLabelGetNumValues(label, &depth));
5048   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5049   PetscFunctionReturn(0);
5050 }
5051 
5052 /*@
5053   DMPlexGetPointDepth - Get the depth of a given point
5054 
5055   Not Collective
5056 
5057   Input Parameters:
5058 + dm    - The DMPlex object
5059 - point - The point
5060 
5061   Output Parameter:
5062 . depth - The depth of the point
5063 
5064   Level: intermediate
5065 
5066 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5067 @*/
5068 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5069 {
5070   PetscFunctionBegin;
5071   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5072   PetscValidIntPointer(depth, 3);
5073   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5074   PetscFunctionReturn(0);
5075 }
5076 
5077 /*@
5078   DMPlexGetPointHeight - Get the height of a given point
5079 
5080   Not Collective
5081 
5082   Input Parameters:
5083 + dm    - The DMPlex object
5084 - point - The point
5085 
5086   Output Parameter:
5087 . height - The height of the point
5088 
5089   Level: intermediate
5090 
5091 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5092 @*/
5093 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5094 {
5095   PetscInt n, pDepth;
5096 
5097   PetscFunctionBegin;
5098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5099   PetscValidIntPointer(height, 3);
5100   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5101   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5102   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5103   PetscFunctionReturn(0);
5104 }
5105 
5106 /*@
5107   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
5108 
5109   Not Collective
5110 
5111   Input Parameter:
5112 . dm - The DMPlex object
5113 
5114   Output Parameter:
5115 . celltypeLabel - The DMLabel recording cell polytope type
5116 
5117   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
5118   DMCreateLabel(dm, "celltype") beforehand.
5119 
5120   Level: developer
5121 
5122 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5123 @*/
5124 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5125 {
5126   PetscFunctionBegin;
5127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5128   PetscValidPointer(celltypeLabel, 2);
5129   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5130   *celltypeLabel = dm->celltypeLabel;
5131   PetscFunctionReturn(0);
5132 }
5133 
5134 /*@
5135   DMPlexGetCellType - Get the polytope type of a given cell
5136 
5137   Not Collective
5138 
5139   Input Parameters:
5140 + dm   - The DMPlex object
5141 - cell - The cell
5142 
5143   Output Parameter:
5144 . celltype - The polytope type of the cell
5145 
5146   Level: intermediate
5147 
5148 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5149 @*/
5150 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5151 {
5152   DM_Plex *mesh = (DM_Plex *)dm->data;
5153   DMLabel  label;
5154   PetscInt ct;
5155 
5156   PetscFunctionBegin;
5157   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5158   PetscValidPointer(celltype, 3);
5159   if (mesh->tr) {
5160     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5161   } else {
5162     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5163     PetscCall(DMLabelGetValue(label, cell, &ct));
5164     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5165     *celltype = (DMPolytopeType)ct;
5166   }
5167   PetscFunctionReturn(0);
5168 }
5169 
5170 /*@
5171   DMPlexSetCellType - Set the polytope type of a given cell
5172 
5173   Not Collective
5174 
5175   Input Parameters:
5176 + dm   - The DMPlex object
5177 . cell - The cell
5178 - celltype - The polytope type of the cell
5179 
5180   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5181   is executed. This function will override the computed type. However, if automatic classification will not succeed
5182   and a user wants to manually specify all types, the classification must be disabled by calling
5183   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5184 
5185   Level: advanced
5186 
5187 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5188 @*/
5189 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5190 {
5191   DMLabel label;
5192 
5193   PetscFunctionBegin;
5194   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5195   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5196   PetscCall(DMLabelSetValue(label, cell, celltype));
5197   PetscFunctionReturn(0);
5198 }
5199 
5200 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5201 {
5202   PetscSection section, s;
5203   Mat          m;
5204   PetscInt     maxHeight;
5205   const char  *prefix;
5206 
5207   PetscFunctionBegin;
5208   PetscCall(DMClone(dm, cdm));
5209   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5210   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5211   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5212   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5213   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5214   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5215   PetscCall(DMSetLocalSection(*cdm, section));
5216   PetscCall(PetscSectionDestroy(&section));
5217   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5218   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5219   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5220   PetscCall(PetscSectionDestroy(&s));
5221   PetscCall(MatDestroy(&m));
5222 
5223   PetscCall(DMSetNumFields(*cdm, 1));
5224   PetscCall(DMCreateDS(*cdm));
5225   (*cdm)->cloneOpts = PETSC_TRUE;
5226   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5227   PetscFunctionReturn(0);
5228 }
5229 
5230 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5231 {
5232   Vec coordsLocal, cellCoordsLocal;
5233   DM  coordsDM, cellCoordsDM;
5234 
5235   PetscFunctionBegin;
5236   *field = NULL;
5237   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5238   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5239   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5240   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5241   if (coordsLocal && coordsDM) {
5242     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5243     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5244   }
5245   PetscFunctionReturn(0);
5246 }
5247 
5248 /*@C
5249   DMPlexGetConeSection - Return a section which describes the layout of cone data
5250 
5251   Not Collective
5252 
5253   Input Parameters:
5254 . dm        - The DMPlex object
5255 
5256   Output Parameter:
5257 . section - The PetscSection object
5258 
5259   Level: developer
5260 
5261 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5262 @*/
5263 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5264 {
5265   DM_Plex *mesh = (DM_Plex *)dm->data;
5266 
5267   PetscFunctionBegin;
5268   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5269   if (section) *section = mesh->coneSection;
5270   PetscFunctionReturn(0);
5271 }
5272 
5273 /*@C
5274   DMPlexGetSupportSection - Return a section which describes the layout of support data
5275 
5276   Not Collective
5277 
5278   Input Parameters:
5279 . dm        - The DMPlex object
5280 
5281   Output Parameter:
5282 . section - The PetscSection object
5283 
5284   Level: developer
5285 
5286 .seealso: `DMPlexGetConeSection()`
5287 @*/
5288 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5289 {
5290   DM_Plex *mesh = (DM_Plex *)dm->data;
5291 
5292   PetscFunctionBegin;
5293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5294   if (section) *section = mesh->supportSection;
5295   PetscFunctionReturn(0);
5296 }
5297 
5298 /*@C
5299   DMPlexGetCones - Return cone data
5300 
5301   Not Collective
5302 
5303   Input Parameters:
5304 . dm        - The DMPlex object
5305 
5306   Output Parameter:
5307 . cones - The cone for each point
5308 
5309   Level: developer
5310 
5311 .seealso: `DMPlexGetConeSection()`
5312 @*/
5313 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5314 {
5315   DM_Plex *mesh = (DM_Plex *)dm->data;
5316 
5317   PetscFunctionBegin;
5318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5319   if (cones) *cones = mesh->cones;
5320   PetscFunctionReturn(0);
5321 }
5322 
5323 /*@C
5324   DMPlexGetConeOrientations - Return cone orientation data
5325 
5326   Not Collective
5327 
5328   Input Parameters:
5329 . dm        - The DMPlex object
5330 
5331   Output Parameter:
5332 . coneOrientations - The array of cone orientations for all points
5333 
5334   Level: developer
5335 
5336   Notes:
5337   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5338 
5339   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5340 
5341 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5342 @*/
5343 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5344 {
5345   DM_Plex *mesh = (DM_Plex *)dm->data;
5346 
5347   PetscFunctionBegin;
5348   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5349   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5350   PetscFunctionReturn(0);
5351 }
5352 
5353 /******************************** FEM Support **********************************/
5354 
5355 /*
5356  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5357  representing a line in the section.
5358 */
5359 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5360 {
5361   PetscFunctionBeginHot;
5362   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5363   if (line < 0) {
5364     *k  = 0;
5365     *Nc = 0;
5366   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5367     *k = 1;
5368   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5369     /* An order k SEM disc has k-1 dofs on an edge */
5370     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5371     *k = *k / *Nc + 1;
5372   }
5373   PetscFunctionReturn(0);
5374 }
5375 
5376 /*@
5377 
5378   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5379   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5380   section provided (or the section of the DM).
5381 
5382   Input Parameters:
5383 + dm      - The DM
5384 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5385 - section - The PetscSection to reorder, or NULL for the default section
5386 
5387   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5388   degree of the basis.
5389 
5390   Example:
5391   A typical interpolated single-quad mesh might order points as
5392 .vb
5393   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5394 
5395   v4 -- e6 -- v3
5396   |           |
5397   e7    c0    e8
5398   |           |
5399   v1 -- e5 -- v2
5400 .ve
5401 
5402   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5403   dofs in the order of points, e.g.,
5404 .vb
5405     c0 -> [0,1,2,3]
5406     v1 -> [4]
5407     ...
5408     e5 -> [8, 9]
5409 .ve
5410 
5411   which corresponds to the dofs
5412 .vb
5413     6   10  11  7
5414     13  2   3   15
5415     12  0   1   14
5416     4   8   9   5
5417 .ve
5418 
5419   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5420 .vb
5421   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5422 .ve
5423 
5424   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5425 .vb
5426    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5427 .ve
5428 
5429   Level: developer
5430 
5431 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5432 @*/
5433 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5434 {
5435   DMLabel   label;
5436   PetscInt  dim, depth = -1, eStart = -1, Nf;
5437   PetscBool vertexchart;
5438 
5439   PetscFunctionBegin;
5440   PetscCall(DMGetDimension(dm, &dim));
5441   if (dim < 1) PetscFunctionReturn(0);
5442   if (point < 0) {
5443     PetscInt sStart, sEnd;
5444 
5445     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5446     point = sEnd - sStart ? sStart : point;
5447   }
5448   PetscCall(DMPlexGetDepthLabel(dm, &label));
5449   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5450   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5451   if (depth == 1) {
5452     eStart = point;
5453   } else if (depth == dim) {
5454     const PetscInt *cone;
5455 
5456     PetscCall(DMPlexGetCone(dm, point, &cone));
5457     if (dim == 2) eStart = cone[0];
5458     else if (dim == 3) {
5459       const PetscInt *cone2;
5460       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5461       eStart = cone2[0];
5462     } 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);
5463   } 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);
5464   { /* Determine whether the chart covers all points or just vertices. */
5465     PetscInt pStart, pEnd, cStart, cEnd;
5466     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5467     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5468     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5469     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5470     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5471   }
5472   PetscCall(PetscSectionGetNumFields(section, &Nf));
5473   for (PetscInt d = 1; d <= dim; d++) {
5474     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5475     PetscInt *perm;
5476 
5477     for (f = 0; f < Nf; ++f) {
5478       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5479       size += PetscPowInt(k + 1, d) * Nc;
5480     }
5481     PetscCall(PetscMalloc1(size, &perm));
5482     for (f = 0; f < Nf; ++f) {
5483       switch (d) {
5484       case 1:
5485         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5486         /*
5487          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5488          We want              [ vtx0; edge of length k-1; vtx1 ]
5489          */
5490         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5491         for (i = 0; i < k - 1; i++)
5492           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5493         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5494         foffset = offset;
5495         break;
5496       case 2:
5497         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5498         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5499         /* The SEM order is
5500 
5501          v_lb, {e_b}, v_rb,
5502          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5503          v_lt, reverse {e_t}, v_rt
5504          */
5505         {
5506           const PetscInt of   = 0;
5507           const PetscInt oeb  = of + PetscSqr(k - 1);
5508           const PetscInt oer  = oeb + (k - 1);
5509           const PetscInt oet  = oer + (k - 1);
5510           const PetscInt oel  = oet + (k - 1);
5511           const PetscInt ovlb = oel + (k - 1);
5512           const PetscInt ovrb = ovlb + 1;
5513           const PetscInt ovrt = ovrb + 1;
5514           const PetscInt ovlt = ovrt + 1;
5515           PetscInt       o;
5516 
5517           /* bottom */
5518           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5519           for (o = oeb; o < oer; ++o)
5520             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5521           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5522           /* middle */
5523           for (i = 0; i < k - 1; ++i) {
5524             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5525             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5526               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5527             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5528           }
5529           /* top */
5530           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5531           for (o = oel - 1; o >= oet; --o)
5532             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5533           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5534           foffset = offset;
5535         }
5536         break;
5537       case 3:
5538         /* The original hex closure is
5539 
5540          {c,
5541          f_b, f_t, f_f, f_b, f_r, f_l,
5542          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5543          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5544          */
5545         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5546         /* The SEM order is
5547          Bottom Slice
5548          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5549          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5550          v_blb, {e_bb}, v_brb,
5551 
5552          Middle Slice (j)
5553          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5554          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5555          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5556 
5557          Top Slice
5558          v_tlf, {e_tf}, v_trf,
5559          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5560          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5561          */
5562         {
5563           const PetscInt oc    = 0;
5564           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5565           const PetscInt oft   = ofb + PetscSqr(k - 1);
5566           const PetscInt off   = oft + PetscSqr(k - 1);
5567           const PetscInt ofk   = off + PetscSqr(k - 1);
5568           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5569           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5570           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5571           const PetscInt oebb  = oebl + (k - 1);
5572           const PetscInt oebr  = oebb + (k - 1);
5573           const PetscInt oebf  = oebr + (k - 1);
5574           const PetscInt oetf  = oebf + (k - 1);
5575           const PetscInt oetr  = oetf + (k - 1);
5576           const PetscInt oetb  = oetr + (k - 1);
5577           const PetscInt oetl  = oetb + (k - 1);
5578           const PetscInt oerf  = oetl + (k - 1);
5579           const PetscInt oelf  = oerf + (k - 1);
5580           const PetscInt oelb  = oelf + (k - 1);
5581           const PetscInt oerb  = oelb + (k - 1);
5582           const PetscInt ovblf = oerb + (k - 1);
5583           const PetscInt ovblb = ovblf + 1;
5584           const PetscInt ovbrb = ovblb + 1;
5585           const PetscInt ovbrf = ovbrb + 1;
5586           const PetscInt ovtlf = ovbrf + 1;
5587           const PetscInt ovtrf = ovtlf + 1;
5588           const PetscInt ovtrb = ovtrf + 1;
5589           const PetscInt ovtlb = ovtrb + 1;
5590           PetscInt       o, n;
5591 
5592           /* Bottom Slice */
5593           /*   bottom */
5594           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5595           for (o = oetf - 1; o >= oebf; --o)
5596             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5597           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5598           /*   middle */
5599           for (i = 0; i < k - 1; ++i) {
5600             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5601             for (n = 0; n < k - 1; ++n) {
5602               o = ofb + n * (k - 1) + i;
5603               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5604             }
5605             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5606           }
5607           /*   top */
5608           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5609           for (o = oebb; o < oebr; ++o)
5610             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5611           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5612 
5613           /* Middle Slice */
5614           for (j = 0; j < k - 1; ++j) {
5615             /*   bottom */
5616             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5617             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5618               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5619             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5620             /*   middle */
5621             for (i = 0; i < k - 1; ++i) {
5622               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5623               for (n = 0; n < k - 1; ++n)
5624                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5625               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5626             }
5627             /*   top */
5628             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5629             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5630               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5631             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5632           }
5633 
5634           /* Top Slice */
5635           /*   bottom */
5636           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5637           for (o = oetf; o < oetr; ++o)
5638             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5639           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5640           /*   middle */
5641           for (i = 0; i < k - 1; ++i) {
5642             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5643             for (n = 0; n < k - 1; ++n)
5644               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5645             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5646           }
5647           /*   top */
5648           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5649           for (o = oetl - 1; o >= oetb; --o)
5650             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5651           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5652 
5653           foffset = offset;
5654         }
5655         break;
5656       default:
5657         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5658       }
5659     }
5660     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5661     /* Check permutation */
5662     {
5663       PetscInt *check;
5664 
5665       PetscCall(PetscMalloc1(size, &check));
5666       for (i = 0; i < size; ++i) {
5667         check[i] = -1;
5668         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5669       }
5670       for (i = 0; i < size; ++i) check[perm[i]] = i;
5671       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5672       PetscCall(PetscFree(check));
5673     }
5674     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5675     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5676       PetscInt *loc_perm;
5677       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5678       for (PetscInt i = 0; i < size; i++) {
5679         loc_perm[i]        = perm[i];
5680         loc_perm[size + i] = size + perm[i];
5681       }
5682       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5683     }
5684   }
5685   PetscFunctionReturn(0);
5686 }
5687 
5688 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5689 {
5690   PetscDS  prob;
5691   PetscInt depth, Nf, h;
5692   DMLabel  label;
5693 
5694   PetscFunctionBeginHot;
5695   PetscCall(DMGetDS(dm, &prob));
5696   Nf      = prob->Nf;
5697   label   = dm->depthLabel;
5698   *dspace = NULL;
5699   if (field < Nf) {
5700     PetscObject disc = prob->disc[field];
5701 
5702     if (disc->classid == PETSCFE_CLASSID) {
5703       PetscDualSpace dsp;
5704 
5705       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5706       PetscCall(DMLabelGetNumValues(label, &depth));
5707       PetscCall(DMLabelGetValue(label, point, &h));
5708       h = depth - 1 - h;
5709       if (h) {
5710         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5711       } else {
5712         *dspace = dsp;
5713       }
5714     }
5715   }
5716   PetscFunctionReturn(0);
5717 }
5718 
5719 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5720 {
5721   PetscScalar       *array;
5722   const PetscScalar *vArray;
5723   const PetscInt    *cone, *coneO;
5724   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5725 
5726   PetscFunctionBeginHot;
5727   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5728   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5729   PetscCall(DMPlexGetCone(dm, point, &cone));
5730   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5731   if (!values || !*values) {
5732     if ((point >= pStart) && (point < pEnd)) {
5733       PetscInt dof;
5734 
5735       PetscCall(PetscSectionGetDof(section, point, &dof));
5736       size += dof;
5737     }
5738     for (p = 0; p < numPoints; ++p) {
5739       const PetscInt cp = cone[p];
5740       PetscInt       dof;
5741 
5742       if ((cp < pStart) || (cp >= pEnd)) continue;
5743       PetscCall(PetscSectionGetDof(section, cp, &dof));
5744       size += dof;
5745     }
5746     if (!values) {
5747       if (csize) *csize = size;
5748       PetscFunctionReturn(0);
5749     }
5750     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5751   } else {
5752     array = *values;
5753   }
5754   size = 0;
5755   PetscCall(VecGetArrayRead(v, &vArray));
5756   if ((point >= pStart) && (point < pEnd)) {
5757     PetscInt           dof, off, d;
5758     const PetscScalar *varr;
5759 
5760     PetscCall(PetscSectionGetDof(section, point, &dof));
5761     PetscCall(PetscSectionGetOffset(section, point, &off));
5762     varr = &vArray[off];
5763     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5764     size += dof;
5765   }
5766   for (p = 0; p < numPoints; ++p) {
5767     const PetscInt     cp = cone[p];
5768     PetscInt           o  = coneO[p];
5769     PetscInt           dof, off, d;
5770     const PetscScalar *varr;
5771 
5772     if ((cp < pStart) || (cp >= pEnd)) continue;
5773     PetscCall(PetscSectionGetDof(section, cp, &dof));
5774     PetscCall(PetscSectionGetOffset(section, cp, &off));
5775     varr = &vArray[off];
5776     if (o >= 0) {
5777       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5778     } else {
5779       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5780     }
5781     size += dof;
5782   }
5783   PetscCall(VecRestoreArrayRead(v, &vArray));
5784   if (!*values) {
5785     if (csize) *csize = size;
5786     *values = array;
5787   } else {
5788     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5789     *csize = size;
5790   }
5791   PetscFunctionReturn(0);
5792 }
5793 
5794 /* Compress out points not in the section */
5795 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5796 {
5797   const PetscInt np = *numPoints;
5798   PetscInt       pStart, pEnd, p, q;
5799 
5800   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5801   for (p = 0, q = 0; p < np; ++p) {
5802     const PetscInt r = points[p * 2];
5803     if ((r >= pStart) && (r < pEnd)) {
5804       points[q * 2]     = r;
5805       points[q * 2 + 1] = points[p * 2 + 1];
5806       ++q;
5807     }
5808   }
5809   *numPoints = q;
5810   return 0;
5811 }
5812 
5813 /* Compressed closure does not apply closure permutation */
5814 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5815 {
5816   const PetscInt *cla = NULL;
5817   PetscInt        np, *pts = NULL;
5818 
5819   PetscFunctionBeginHot;
5820   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5821   if (*clPoints) {
5822     PetscInt dof, off;
5823 
5824     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5825     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5826     PetscCall(ISGetIndices(*clPoints, &cla));
5827     np  = dof / 2;
5828     pts = (PetscInt *)&cla[off];
5829   } else {
5830     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5831     PetscCall(CompressPoints_Private(section, &np, pts));
5832   }
5833   *numPoints = np;
5834   *points    = pts;
5835   *clp       = cla;
5836   PetscFunctionReturn(0);
5837 }
5838 
5839 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5840 {
5841   PetscFunctionBeginHot;
5842   if (!*clPoints) {
5843     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5844   } else {
5845     PetscCall(ISRestoreIndices(*clPoints, clp));
5846   }
5847   *numPoints = 0;
5848   *points    = NULL;
5849   *clSec     = NULL;
5850   *clPoints  = NULL;
5851   *clp       = NULL;
5852   PetscFunctionReturn(0);
5853 }
5854 
5855 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5856 {
5857   PetscInt            offset = 0, p;
5858   const PetscInt    **perms  = NULL;
5859   const PetscScalar **flips  = NULL;
5860 
5861   PetscFunctionBeginHot;
5862   *size = 0;
5863   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5864   for (p = 0; p < numPoints; p++) {
5865     const PetscInt     point = points[2 * p];
5866     const PetscInt    *perm  = perms ? perms[p] : NULL;
5867     const PetscScalar *flip  = flips ? flips[p] : NULL;
5868     PetscInt           dof, off, d;
5869     const PetscScalar *varr;
5870 
5871     PetscCall(PetscSectionGetDof(section, point, &dof));
5872     PetscCall(PetscSectionGetOffset(section, point, &off));
5873     varr = &vArray[off];
5874     if (clperm) {
5875       if (perm) {
5876         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5877       } else {
5878         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5879       }
5880       if (flip) {
5881         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5882       }
5883     } else {
5884       if (perm) {
5885         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5886       } else {
5887         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5888       }
5889       if (flip) {
5890         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5891       }
5892     }
5893     offset += dof;
5894   }
5895   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5896   *size = offset;
5897   PetscFunctionReturn(0);
5898 }
5899 
5900 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[])
5901 {
5902   PetscInt offset = 0, f;
5903 
5904   PetscFunctionBeginHot;
5905   *size = 0;
5906   for (f = 0; f < numFields; ++f) {
5907     PetscInt            p;
5908     const PetscInt    **perms = NULL;
5909     const PetscScalar **flips = NULL;
5910 
5911     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5912     for (p = 0; p < numPoints; p++) {
5913       const PetscInt     point = points[2 * p];
5914       PetscInt           fdof, foff, b;
5915       const PetscScalar *varr;
5916       const PetscInt    *perm = perms ? perms[p] : NULL;
5917       const PetscScalar *flip = flips ? flips[p] : NULL;
5918 
5919       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5920       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5921       varr = &vArray[foff];
5922       if (clperm) {
5923         if (perm) {
5924           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5925         } else {
5926           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5927         }
5928         if (flip) {
5929           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5930         }
5931       } else {
5932         if (perm) {
5933           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5934         } else {
5935           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5936         }
5937         if (flip) {
5938           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5939         }
5940       }
5941       offset += fdof;
5942     }
5943     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5944   }
5945   *size = offset;
5946   PetscFunctionReturn(0);
5947 }
5948 
5949 /*@C
5950   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5951 
5952   Not collective
5953 
5954   Input Parameters:
5955 + dm - The DM
5956 . section - The section describing the layout in v, or NULL to use the default section
5957 . v - The local vector
5958 - point - The point in the DM
5959 
5960   Input/Output Parameters:
5961 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5962 - values - An array to use for the values, or NULL to have it allocated automatically;
5963            if the user provided NULL, it is a borrowed array and should not be freed
5964 
5965 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5966 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5967 $ assembly function, and a user may already have allocated storage for this operation.
5968 $
5969 $ A typical use could be
5970 $
5971 $  values = NULL;
5972 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5973 $  for (cl = 0; cl < clSize; ++cl) {
5974 $    <Compute on closure>
5975 $  }
5976 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5977 $
5978 $ or
5979 $
5980 $  PetscMalloc1(clMaxSize, &values);
5981 $  for (p = pStart; p < pEnd; ++p) {
5982 $    clSize = clMaxSize;
5983 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5984 $    for (cl = 0; cl < clSize; ++cl) {
5985 $      <Compute on closure>
5986 $    }
5987 $  }
5988 $  PetscFree(values);
5989 
5990   Fortran Notes:
5991   Since it returns an array, this routine is only available in Fortran 90, and you must
5992   include petsc.h90 in your code.
5993 
5994   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5995 
5996   Level: intermediate
5997 
5998 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5999 @*/
6000 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6001 {
6002   PetscSection    clSection;
6003   IS              clPoints;
6004   PetscInt       *points = NULL;
6005   const PetscInt *clp, *perm;
6006   PetscInt        depth, numFields, numPoints, asize;
6007 
6008   PetscFunctionBeginHot;
6009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6010   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6011   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6012   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6013   PetscCall(DMPlexGetDepth(dm, &depth));
6014   PetscCall(PetscSectionGetNumFields(section, &numFields));
6015   if (depth == 1 && numFields < 2) {
6016     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6017     PetscFunctionReturn(0);
6018   }
6019   /* Get points */
6020   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6021   /* Get sizes */
6022   asize = 0;
6023   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6024     PetscInt dof;
6025     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6026     asize += dof;
6027   }
6028   if (values) {
6029     const PetscScalar *vArray;
6030     PetscInt           size;
6031 
6032     if (*values) {
6033       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);
6034     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6035     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6036     PetscCall(VecGetArrayRead(v, &vArray));
6037     /* Get values */
6038     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6039     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6040     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6041     /* Cleanup array */
6042     PetscCall(VecRestoreArrayRead(v, &vArray));
6043   }
6044   if (csize) *csize = asize;
6045   /* Cleanup points */
6046   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6047   PetscFunctionReturn(0);
6048 }
6049 
6050 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6051 {
6052   DMLabel            depthLabel;
6053   PetscSection       clSection;
6054   IS                 clPoints;
6055   PetscScalar       *array;
6056   const PetscScalar *vArray;
6057   PetscInt          *points = NULL;
6058   const PetscInt    *clp, *perm = NULL;
6059   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6060 
6061   PetscFunctionBeginHot;
6062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6063   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6064   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6065   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6066   PetscCall(DMPlexGetDepth(dm, &mdepth));
6067   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6068   PetscCall(PetscSectionGetNumFields(section, &numFields));
6069   if (mdepth == 1 && numFields < 2) {
6070     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6071     PetscFunctionReturn(0);
6072   }
6073   /* Get points */
6074   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6075   for (clsize = 0, p = 0; p < Np; p++) {
6076     PetscInt dof;
6077     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6078     clsize += dof;
6079   }
6080   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6081   /* Filter points */
6082   for (p = 0; p < numPoints * 2; p += 2) {
6083     PetscInt dep;
6084 
6085     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6086     if (dep != depth) continue;
6087     points[Np * 2 + 0] = points[p];
6088     points[Np * 2 + 1] = points[p + 1];
6089     ++Np;
6090   }
6091   /* Get array */
6092   if (!values || !*values) {
6093     PetscInt asize = 0, dof;
6094 
6095     for (p = 0; p < Np * 2; p += 2) {
6096       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6097       asize += dof;
6098     }
6099     if (!values) {
6100       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6101       if (csize) *csize = asize;
6102       PetscFunctionReturn(0);
6103     }
6104     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6105   } else {
6106     array = *values;
6107   }
6108   PetscCall(VecGetArrayRead(v, &vArray));
6109   /* Get values */
6110   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6111   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6112   /* Cleanup points */
6113   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6114   /* Cleanup array */
6115   PetscCall(VecRestoreArrayRead(v, &vArray));
6116   if (!*values) {
6117     if (csize) *csize = size;
6118     *values = array;
6119   } else {
6120     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6121     *csize = size;
6122   }
6123   PetscFunctionReturn(0);
6124 }
6125 
6126 /*@C
6127   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6128 
6129   Not collective
6130 
6131   Input Parameters:
6132 + dm - The DM
6133 . section - The section describing the layout in v, or NULL to use the default section
6134 . v - The local vector
6135 . point - The point in the DM
6136 . csize - The number of values in the closure, or NULL
6137 - values - The array of values, which is a borrowed array and should not be freed
6138 
6139   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
6140 
6141   Fortran Notes:
6142   Since it returns an array, this routine is only available in Fortran 90, and you must
6143   include petsc.h90 in your code.
6144 
6145   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
6146 
6147   Level: intermediate
6148 
6149 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6150 @*/
6151 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6152 {
6153   PetscInt size = 0;
6154 
6155   PetscFunctionBegin;
6156   /* Should work without recalculating size */
6157   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6158   *values = NULL;
6159   PetscFunctionReturn(0);
6160 }
6161 
6162 static inline void add(PetscScalar *x, PetscScalar y)
6163 {
6164   *x += y;
6165 }
6166 static inline void insert(PetscScalar *x, PetscScalar y)
6167 {
6168   *x = y;
6169 }
6170 
6171 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[])
6172 {
6173   PetscInt        cdof;  /* The number of constraints on this point */
6174   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6175   PetscScalar    *a;
6176   PetscInt        off, cind = 0, k;
6177 
6178   PetscFunctionBegin;
6179   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6180   PetscCall(PetscSectionGetOffset(section, point, &off));
6181   a = &array[off];
6182   if (!cdof || setBC) {
6183     if (clperm) {
6184       if (perm) {
6185         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6186       } else {
6187         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6188       }
6189     } else {
6190       if (perm) {
6191         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6192       } else {
6193         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6194       }
6195     }
6196   } else {
6197     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6198     if (clperm) {
6199       if (perm) {
6200         for (k = 0; k < dof; ++k) {
6201           if ((cind < cdof) && (k == cdofs[cind])) {
6202             ++cind;
6203             continue;
6204           }
6205           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6206         }
6207       } else {
6208         for (k = 0; k < dof; ++k) {
6209           if ((cind < cdof) && (k == cdofs[cind])) {
6210             ++cind;
6211             continue;
6212           }
6213           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6214         }
6215       }
6216     } else {
6217       if (perm) {
6218         for (k = 0; k < dof; ++k) {
6219           if ((cind < cdof) && (k == cdofs[cind])) {
6220             ++cind;
6221             continue;
6222           }
6223           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6224         }
6225       } else {
6226         for (k = 0; k < dof; ++k) {
6227           if ((cind < cdof) && (k == cdofs[cind])) {
6228             ++cind;
6229             continue;
6230           }
6231           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6232         }
6233       }
6234     }
6235   }
6236   PetscFunctionReturn(0);
6237 }
6238 
6239 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[])
6240 {
6241   PetscInt        cdof;  /* The number of constraints on this point */
6242   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6243   PetscScalar    *a;
6244   PetscInt        off, cind = 0, k;
6245 
6246   PetscFunctionBegin;
6247   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6248   PetscCall(PetscSectionGetOffset(section, point, &off));
6249   a = &array[off];
6250   if (cdof) {
6251     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6252     if (clperm) {
6253       if (perm) {
6254         for (k = 0; k < dof; ++k) {
6255           if ((cind < cdof) && (k == cdofs[cind])) {
6256             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6257             cind++;
6258           }
6259         }
6260       } else {
6261         for (k = 0; k < dof; ++k) {
6262           if ((cind < cdof) && (k == cdofs[cind])) {
6263             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6264             cind++;
6265           }
6266         }
6267       }
6268     } else {
6269       if (perm) {
6270         for (k = 0; k < dof; ++k) {
6271           if ((cind < cdof) && (k == cdofs[cind])) {
6272             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6273             cind++;
6274           }
6275         }
6276       } else {
6277         for (k = 0; k < dof; ++k) {
6278           if ((cind < cdof) && (k == cdofs[cind])) {
6279             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6280             cind++;
6281           }
6282         }
6283       }
6284     }
6285   }
6286   PetscFunctionReturn(0);
6287 }
6288 
6289 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[])
6290 {
6291   PetscScalar    *a;
6292   PetscInt        fdof, foff, fcdof, foffset = *offset;
6293   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6294   PetscInt        cind = 0, b;
6295 
6296   PetscFunctionBegin;
6297   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6298   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6299   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6300   a = &array[foff];
6301   if (!fcdof || setBC) {
6302     if (clperm) {
6303       if (perm) {
6304         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6305       } else {
6306         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6307       }
6308     } else {
6309       if (perm) {
6310         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6311       } else {
6312         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6313       }
6314     }
6315   } else {
6316     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6317     if (clperm) {
6318       if (perm) {
6319         for (b = 0; b < fdof; b++) {
6320           if ((cind < fcdof) && (b == fcdofs[cind])) {
6321             ++cind;
6322             continue;
6323           }
6324           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6325         }
6326       } else {
6327         for (b = 0; b < fdof; b++) {
6328           if ((cind < fcdof) && (b == fcdofs[cind])) {
6329             ++cind;
6330             continue;
6331           }
6332           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6333         }
6334       }
6335     } else {
6336       if (perm) {
6337         for (b = 0; b < fdof; b++) {
6338           if ((cind < fcdof) && (b == fcdofs[cind])) {
6339             ++cind;
6340             continue;
6341           }
6342           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6343         }
6344       } else {
6345         for (b = 0; b < fdof; b++) {
6346           if ((cind < fcdof) && (b == fcdofs[cind])) {
6347             ++cind;
6348             continue;
6349           }
6350           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6351         }
6352       }
6353     }
6354   }
6355   *offset += fdof;
6356   PetscFunctionReturn(0);
6357 }
6358 
6359 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[])
6360 {
6361   PetscScalar    *a;
6362   PetscInt        fdof, foff, fcdof, foffset = *offset;
6363   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6364   PetscInt        Nc, cind = 0, ncind = 0, b;
6365   PetscBool       ncSet, fcSet;
6366 
6367   PetscFunctionBegin;
6368   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6369   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6370   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6371   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6372   a = &array[foff];
6373   if (fcdof) {
6374     /* We just override fcdof and fcdofs with Ncc and comps */
6375     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6376     if (clperm) {
6377       if (perm) {
6378         if (comps) {
6379           for (b = 0; b < fdof; b++) {
6380             ncSet = fcSet = PETSC_FALSE;
6381             if (b % Nc == comps[ncind]) {
6382               ncind = (ncind + 1) % Ncc;
6383               ncSet = PETSC_TRUE;
6384             }
6385             if ((cind < fcdof) && (b == fcdofs[cind])) {
6386               ++cind;
6387               fcSet = PETSC_TRUE;
6388             }
6389             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6390           }
6391         } else {
6392           for (b = 0; b < fdof; b++) {
6393             if ((cind < fcdof) && (b == fcdofs[cind])) {
6394               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6395               ++cind;
6396             }
6397           }
6398         }
6399       } else {
6400         if (comps) {
6401           for (b = 0; b < fdof; b++) {
6402             ncSet = fcSet = PETSC_FALSE;
6403             if (b % Nc == comps[ncind]) {
6404               ncind = (ncind + 1) % Ncc;
6405               ncSet = PETSC_TRUE;
6406             }
6407             if ((cind < fcdof) && (b == fcdofs[cind])) {
6408               ++cind;
6409               fcSet = PETSC_TRUE;
6410             }
6411             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6412           }
6413         } else {
6414           for (b = 0; b < fdof; b++) {
6415             if ((cind < fcdof) && (b == fcdofs[cind])) {
6416               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6417               ++cind;
6418             }
6419           }
6420         }
6421       }
6422     } else {
6423       if (perm) {
6424         if (comps) {
6425           for (b = 0; b < fdof; b++) {
6426             ncSet = fcSet = PETSC_FALSE;
6427             if (b % Nc == comps[ncind]) {
6428               ncind = (ncind + 1) % Ncc;
6429               ncSet = PETSC_TRUE;
6430             }
6431             if ((cind < fcdof) && (b == fcdofs[cind])) {
6432               ++cind;
6433               fcSet = PETSC_TRUE;
6434             }
6435             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6436           }
6437         } else {
6438           for (b = 0; b < fdof; b++) {
6439             if ((cind < fcdof) && (b == fcdofs[cind])) {
6440               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6441               ++cind;
6442             }
6443           }
6444         }
6445       } else {
6446         if (comps) {
6447           for (b = 0; b < fdof; b++) {
6448             ncSet = fcSet = PETSC_FALSE;
6449             if (b % Nc == comps[ncind]) {
6450               ncind = (ncind + 1) % Ncc;
6451               ncSet = PETSC_TRUE;
6452             }
6453             if ((cind < fcdof) && (b == fcdofs[cind])) {
6454               ++cind;
6455               fcSet = PETSC_TRUE;
6456             }
6457             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6458           }
6459         } else {
6460           for (b = 0; b < fdof; b++) {
6461             if ((cind < fcdof) && (b == fcdofs[cind])) {
6462               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6463               ++cind;
6464             }
6465           }
6466         }
6467       }
6468     }
6469   }
6470   *offset += fdof;
6471   PetscFunctionReturn(0);
6472 }
6473 
6474 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6475 {
6476   PetscScalar    *array;
6477   const PetscInt *cone, *coneO;
6478   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6479 
6480   PetscFunctionBeginHot;
6481   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6482   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6483   PetscCall(DMPlexGetCone(dm, point, &cone));
6484   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6485   PetscCall(VecGetArray(v, &array));
6486   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6487     const PetscInt cp = !p ? point : cone[p - 1];
6488     const PetscInt o  = !p ? 0 : coneO[p - 1];
6489 
6490     if ((cp < pStart) || (cp >= pEnd)) {
6491       dof = 0;
6492       continue;
6493     }
6494     PetscCall(PetscSectionGetDof(section, cp, &dof));
6495     /* ADD_VALUES */
6496     {
6497       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6498       PetscScalar    *a;
6499       PetscInt        cdof, coff, cind = 0, k;
6500 
6501       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6502       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6503       a = &array[coff];
6504       if (!cdof) {
6505         if (o >= 0) {
6506           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6507         } else {
6508           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6509         }
6510       } else {
6511         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6512         if (o >= 0) {
6513           for (k = 0; k < dof; ++k) {
6514             if ((cind < cdof) && (k == cdofs[cind])) {
6515               ++cind;
6516               continue;
6517             }
6518             a[k] += values[off + k];
6519           }
6520         } else {
6521           for (k = 0; k < dof; ++k) {
6522             if ((cind < cdof) && (k == cdofs[cind])) {
6523               ++cind;
6524               continue;
6525             }
6526             a[k] += values[off + dof - k - 1];
6527           }
6528         }
6529       }
6530     }
6531   }
6532   PetscCall(VecRestoreArray(v, &array));
6533   PetscFunctionReturn(0);
6534 }
6535 
6536 /*@C
6537   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6538 
6539   Not collective
6540 
6541   Input Parameters:
6542 + dm - The DM
6543 . section - The section describing the layout in v, or NULL to use the default section
6544 . v - The local vector
6545 . point - The point in the DM
6546 . values - The array of values
6547 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6548          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6549 
6550   Fortran Notes:
6551   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6552 
6553   Level: intermediate
6554 
6555 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6556 @*/
6557 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6558 {
6559   PetscSection    clSection;
6560   IS              clPoints;
6561   PetscScalar    *array;
6562   PetscInt       *points = NULL;
6563   const PetscInt *clp, *clperm = NULL;
6564   PetscInt        depth, numFields, numPoints, p, clsize;
6565 
6566   PetscFunctionBeginHot;
6567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6568   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6569   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6570   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6571   PetscCall(DMPlexGetDepth(dm, &depth));
6572   PetscCall(PetscSectionGetNumFields(section, &numFields));
6573   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6574     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6575     PetscFunctionReturn(0);
6576   }
6577   /* Get points */
6578   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6579   for (clsize = 0, p = 0; p < numPoints; p++) {
6580     PetscInt dof;
6581     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6582     clsize += dof;
6583   }
6584   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6585   /* Get array */
6586   PetscCall(VecGetArray(v, &array));
6587   /* Get values */
6588   if (numFields > 0) {
6589     PetscInt offset = 0, f;
6590     for (f = 0; f < numFields; ++f) {
6591       const PetscInt    **perms = NULL;
6592       const PetscScalar **flips = NULL;
6593 
6594       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6595       switch (mode) {
6596       case INSERT_VALUES:
6597         for (p = 0; p < numPoints; p++) {
6598           const PetscInt     point = points[2 * p];
6599           const PetscInt    *perm  = perms ? perms[p] : NULL;
6600           const PetscScalar *flip  = flips ? flips[p] : NULL;
6601           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6602         }
6603         break;
6604       case INSERT_ALL_VALUES:
6605         for (p = 0; p < numPoints; p++) {
6606           const PetscInt     point = points[2 * p];
6607           const PetscInt    *perm  = perms ? perms[p] : NULL;
6608           const PetscScalar *flip  = flips ? flips[p] : NULL;
6609           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6610         }
6611         break;
6612       case INSERT_BC_VALUES:
6613         for (p = 0; p < numPoints; p++) {
6614           const PetscInt     point = points[2 * p];
6615           const PetscInt    *perm  = perms ? perms[p] : NULL;
6616           const PetscScalar *flip  = flips ? flips[p] : NULL;
6617           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6618         }
6619         break;
6620       case ADD_VALUES:
6621         for (p = 0; p < numPoints; p++) {
6622           const PetscInt     point = points[2 * p];
6623           const PetscInt    *perm  = perms ? perms[p] : NULL;
6624           const PetscScalar *flip  = flips ? flips[p] : NULL;
6625           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6626         }
6627         break;
6628       case ADD_ALL_VALUES:
6629         for (p = 0; p < numPoints; p++) {
6630           const PetscInt     point = points[2 * p];
6631           const PetscInt    *perm  = perms ? perms[p] : NULL;
6632           const PetscScalar *flip  = flips ? flips[p] : NULL;
6633           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6634         }
6635         break;
6636       case ADD_BC_VALUES:
6637         for (p = 0; p < numPoints; p++) {
6638           const PetscInt     point = points[2 * p];
6639           const PetscInt    *perm  = perms ? perms[p] : NULL;
6640           const PetscScalar *flip  = flips ? flips[p] : NULL;
6641           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6642         }
6643         break;
6644       default:
6645         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6646       }
6647       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6648     }
6649   } else {
6650     PetscInt            dof, off;
6651     const PetscInt    **perms = NULL;
6652     const PetscScalar **flips = NULL;
6653 
6654     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6655     switch (mode) {
6656     case INSERT_VALUES:
6657       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6658         const PetscInt     point = points[2 * p];
6659         const PetscInt    *perm  = perms ? perms[p] : NULL;
6660         const PetscScalar *flip  = flips ? flips[p] : NULL;
6661         PetscCall(PetscSectionGetDof(section, point, &dof));
6662         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6663       }
6664       break;
6665     case INSERT_ALL_VALUES:
6666       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6667         const PetscInt     point = points[2 * p];
6668         const PetscInt    *perm  = perms ? perms[p] : NULL;
6669         const PetscScalar *flip  = flips ? flips[p] : NULL;
6670         PetscCall(PetscSectionGetDof(section, point, &dof));
6671         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6672       }
6673       break;
6674     case INSERT_BC_VALUES:
6675       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6676         const PetscInt     point = points[2 * p];
6677         const PetscInt    *perm  = perms ? perms[p] : NULL;
6678         const PetscScalar *flip  = flips ? flips[p] : NULL;
6679         PetscCall(PetscSectionGetDof(section, point, &dof));
6680         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6681       }
6682       break;
6683     case ADD_VALUES:
6684       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6685         const PetscInt     point = points[2 * p];
6686         const PetscInt    *perm  = perms ? perms[p] : NULL;
6687         const PetscScalar *flip  = flips ? flips[p] : NULL;
6688         PetscCall(PetscSectionGetDof(section, point, &dof));
6689         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6690       }
6691       break;
6692     case ADD_ALL_VALUES:
6693       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6694         const PetscInt     point = points[2 * p];
6695         const PetscInt    *perm  = perms ? perms[p] : NULL;
6696         const PetscScalar *flip  = flips ? flips[p] : NULL;
6697         PetscCall(PetscSectionGetDof(section, point, &dof));
6698         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6699       }
6700       break;
6701     case ADD_BC_VALUES:
6702       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6703         const PetscInt     point = points[2 * p];
6704         const PetscInt    *perm  = perms ? perms[p] : NULL;
6705         const PetscScalar *flip  = flips ? flips[p] : NULL;
6706         PetscCall(PetscSectionGetDof(section, point, &dof));
6707         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6708       }
6709       break;
6710     default:
6711       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6712     }
6713     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6714   }
6715   /* Cleanup points */
6716   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6717   /* Cleanup array */
6718   PetscCall(VecRestoreArray(v, &array));
6719   PetscFunctionReturn(0);
6720 }
6721 
6722 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6723 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6724 {
6725   PetscFunctionBegin;
6726   *contains = PETSC_TRUE;
6727   if (label) {
6728     PetscInt fdof;
6729 
6730     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6731     if (!*contains) {
6732       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6733       *offset += fdof;
6734       PetscFunctionReturn(0);
6735     }
6736   }
6737   PetscFunctionReturn(0);
6738 }
6739 
6740 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6741 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)
6742 {
6743   PetscSection    clSection;
6744   IS              clPoints;
6745   PetscScalar    *array;
6746   PetscInt       *points = NULL;
6747   const PetscInt *clp;
6748   PetscInt        numFields, numPoints, p;
6749   PetscInt        offset = 0, f;
6750 
6751   PetscFunctionBeginHot;
6752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6753   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6754   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6755   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6756   PetscCall(PetscSectionGetNumFields(section, &numFields));
6757   /* Get points */
6758   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6759   /* Get array */
6760   PetscCall(VecGetArray(v, &array));
6761   /* Get values */
6762   for (f = 0; f < numFields; ++f) {
6763     const PetscInt    **perms = NULL;
6764     const PetscScalar **flips = NULL;
6765     PetscBool           contains;
6766 
6767     if (!fieldActive[f]) {
6768       for (p = 0; p < numPoints * 2; p += 2) {
6769         PetscInt fdof;
6770         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6771         offset += fdof;
6772       }
6773       continue;
6774     }
6775     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6776     switch (mode) {
6777     case INSERT_VALUES:
6778       for (p = 0; p < numPoints; p++) {
6779         const PetscInt     point = points[2 * p];
6780         const PetscInt    *perm  = perms ? perms[p] : NULL;
6781         const PetscScalar *flip  = flips ? flips[p] : NULL;
6782         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6783         if (!contains) continue;
6784         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6785       }
6786       break;
6787     case INSERT_ALL_VALUES:
6788       for (p = 0; p < numPoints; p++) {
6789         const PetscInt     point = points[2 * p];
6790         const PetscInt    *perm  = perms ? perms[p] : NULL;
6791         const PetscScalar *flip  = flips ? flips[p] : NULL;
6792         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6793         if (!contains) continue;
6794         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6795       }
6796       break;
6797     case INSERT_BC_VALUES:
6798       for (p = 0; p < numPoints; p++) {
6799         const PetscInt     point = points[2 * p];
6800         const PetscInt    *perm  = perms ? perms[p] : NULL;
6801         const PetscScalar *flip  = flips ? flips[p] : NULL;
6802         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6803         if (!contains) continue;
6804         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6805       }
6806       break;
6807     case ADD_VALUES:
6808       for (p = 0; p < numPoints; p++) {
6809         const PetscInt     point = points[2 * p];
6810         const PetscInt    *perm  = perms ? perms[p] : NULL;
6811         const PetscScalar *flip  = flips ? flips[p] : NULL;
6812         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6813         if (!contains) continue;
6814         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6815       }
6816       break;
6817     case ADD_ALL_VALUES:
6818       for (p = 0; p < numPoints; p++) {
6819         const PetscInt     point = points[2 * p];
6820         const PetscInt    *perm  = perms ? perms[p] : NULL;
6821         const PetscScalar *flip  = flips ? flips[p] : NULL;
6822         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6823         if (!contains) continue;
6824         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6825       }
6826       break;
6827     default:
6828       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6829     }
6830     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6831   }
6832   /* Cleanup points */
6833   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6834   /* Cleanup array */
6835   PetscCall(VecRestoreArray(v, &array));
6836   PetscFunctionReturn(0);
6837 }
6838 
6839 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6840 {
6841   PetscMPIInt rank;
6842   PetscInt    i, j;
6843 
6844   PetscFunctionBegin;
6845   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6846   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6847   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6848   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6849   numCIndices = numCIndices ? numCIndices : numRIndices;
6850   if (!values) PetscFunctionReturn(0);
6851   for (i = 0; i < numRIndices; i++) {
6852     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6853     for (j = 0; j < numCIndices; j++) {
6854 #if defined(PETSC_USE_COMPLEX)
6855       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6856 #else
6857       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6858 #endif
6859     }
6860     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6861   }
6862   PetscFunctionReturn(0);
6863 }
6864 
6865 /*
6866   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6867 
6868   Input Parameters:
6869 + section - The section for this data layout
6870 . islocal - Is the section (and thus indices being requested) local or global?
6871 . point   - The point contributing dofs with these indices
6872 . off     - The global offset of this point
6873 . loff    - The local offset of each field
6874 . setBC   - The flag determining whether to include indices of boundary values
6875 . perm    - A permutation of the dofs on this point, or NULL
6876 - indperm - A permutation of the entire indices array, or NULL
6877 
6878   Output Parameter:
6879 . indices - Indices for dofs on this point
6880 
6881   Level: developer
6882 
6883   Note: The indices could be local or global, depending on the value of 'off'.
6884 */
6885 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6886 {
6887   PetscInt        dof;   /* The number of unknowns on this point */
6888   PetscInt        cdof;  /* The number of constraints on this point */
6889   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6890   PetscInt        cind = 0, k;
6891 
6892   PetscFunctionBegin;
6893   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6894   PetscCall(PetscSectionGetDof(section, point, &dof));
6895   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6896   if (!cdof || setBC) {
6897     for (k = 0; k < dof; ++k) {
6898       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6899       const PetscInt ind    = indperm ? indperm[preind] : preind;
6900 
6901       indices[ind] = off + k;
6902     }
6903   } else {
6904     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6905     for (k = 0; k < dof; ++k) {
6906       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6907       const PetscInt ind    = indperm ? indperm[preind] : preind;
6908 
6909       if ((cind < cdof) && (k == cdofs[cind])) {
6910         /* Insert check for returning constrained indices */
6911         indices[ind] = -(off + k + 1);
6912         ++cind;
6913       } else {
6914         indices[ind] = off + k - (islocal ? 0 : cind);
6915       }
6916     }
6917   }
6918   *loff += dof;
6919   PetscFunctionReturn(0);
6920 }
6921 
6922 /*
6923  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6924 
6925  Input Parameters:
6926 + section - a section (global or local)
6927 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6928 . point - point within section
6929 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6930 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6931 . setBC - identify constrained (boundary condition) points via involution.
6932 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6933 . permsoff - offset
6934 - indperm - index permutation
6935 
6936  Output Parameter:
6937 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6938 . indices - array to hold indices (as defined by section) of each dof associated with point
6939 
6940  Notes:
6941  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6942  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6943  in the local vector.
6944 
6945  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6946  significant).  It is invalid to call with a global section and setBC=true.
6947 
6948  Developer Note:
6949  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6950  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6951  offset could be obtained from the section instead of passing it explicitly as we do now.
6952 
6953  Example:
6954  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6955  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6956  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6957  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.
6958 
6959  Level: developer
6960 */
6961 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[])
6962 {
6963   PetscInt numFields, foff, f;
6964 
6965   PetscFunctionBegin;
6966   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6967   PetscCall(PetscSectionGetNumFields(section, &numFields));
6968   for (f = 0, foff = 0; f < numFields; ++f) {
6969     PetscInt        fdof, cfdof;
6970     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6971     PetscInt        cind = 0, b;
6972     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6973 
6974     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6975     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6976     if (!cfdof || setBC) {
6977       for (b = 0; b < fdof; ++b) {
6978         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6979         const PetscInt ind    = indperm ? indperm[preind] : preind;
6980 
6981         indices[ind] = off + foff + b;
6982       }
6983     } else {
6984       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6985       for (b = 0; b < fdof; ++b) {
6986         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6987         const PetscInt ind    = indperm ? indperm[preind] : preind;
6988 
6989         if ((cind < cfdof) && (b == fcdofs[cind])) {
6990           indices[ind] = -(off + foff + b + 1);
6991           ++cind;
6992         } else {
6993           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6994         }
6995       }
6996     }
6997     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6998     foffs[f] += fdof;
6999   }
7000   PetscFunctionReturn(0);
7001 }
7002 
7003 /*
7004   This version believes the globalSection offsets for each field, rather than just the point offset
7005 
7006  . foffs - The offset into 'indices' for each field, since it is segregated by field
7007 
7008  Notes:
7009  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7010  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7011 */
7012 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7013 {
7014   PetscInt numFields, foff, f;
7015 
7016   PetscFunctionBegin;
7017   PetscCall(PetscSectionGetNumFields(section, &numFields));
7018   for (f = 0; f < numFields; ++f) {
7019     PetscInt        fdof, cfdof;
7020     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7021     PetscInt        cind = 0, b;
7022     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7023 
7024     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7025     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7026     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7027     if (!cfdof) {
7028       for (b = 0; b < fdof; ++b) {
7029         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7030         const PetscInt ind    = indperm ? indperm[preind] : preind;
7031 
7032         indices[ind] = foff + b;
7033       }
7034     } else {
7035       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7036       for (b = 0; b < fdof; ++b) {
7037         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7038         const PetscInt ind    = indperm ? indperm[preind] : preind;
7039 
7040         if ((cind < cfdof) && (b == fcdofs[cind])) {
7041           indices[ind] = -(foff + b + 1);
7042           ++cind;
7043         } else {
7044           indices[ind] = foff + b - cind;
7045         }
7046       }
7047     }
7048     foffs[f] += fdof;
7049   }
7050   PetscFunctionReturn(0);
7051 }
7052 
7053 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)
7054 {
7055   Mat             cMat;
7056   PetscSection    aSec, cSec;
7057   IS              aIS;
7058   PetscInt        aStart = -1, aEnd = -1;
7059   const PetscInt *anchors;
7060   PetscInt        numFields, f, p, q, newP = 0;
7061   PetscInt        newNumPoints = 0, newNumIndices = 0;
7062   PetscInt       *newPoints, *indices, *newIndices;
7063   PetscInt        maxAnchor, maxDof;
7064   PetscInt        newOffsets[32];
7065   PetscInt       *pointMatOffsets[32];
7066   PetscInt       *newPointOffsets[32];
7067   PetscScalar    *pointMat[32];
7068   PetscScalar    *newValues      = NULL, *tmpValues;
7069   PetscBool       anyConstrained = PETSC_FALSE;
7070 
7071   PetscFunctionBegin;
7072   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7073   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7074   PetscCall(PetscSectionGetNumFields(section, &numFields));
7075 
7076   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7077   /* if there are point-to-point constraints */
7078   if (aSec) {
7079     PetscCall(PetscArrayzero(newOffsets, 32));
7080     PetscCall(ISGetIndices(aIS, &anchors));
7081     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7082     /* figure out how many points are going to be in the new element matrix
7083      * (we allow double counting, because it's all just going to be summed
7084      * into the global matrix anyway) */
7085     for (p = 0; p < 2 * numPoints; p += 2) {
7086       PetscInt b    = points[p];
7087       PetscInt bDof = 0, bSecDof;
7088 
7089       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7090       if (!bSecDof) continue;
7091       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7092       if (bDof) {
7093         /* this point is constrained */
7094         /* it is going to be replaced by its anchors */
7095         PetscInt bOff, q;
7096 
7097         anyConstrained = PETSC_TRUE;
7098         newNumPoints += bDof;
7099         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7100         for (q = 0; q < bDof; q++) {
7101           PetscInt a = anchors[bOff + q];
7102           PetscInt aDof;
7103 
7104           PetscCall(PetscSectionGetDof(section, a, &aDof));
7105           newNumIndices += aDof;
7106           for (f = 0; f < numFields; ++f) {
7107             PetscInt fDof;
7108 
7109             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7110             newOffsets[f + 1] += fDof;
7111           }
7112         }
7113       } else {
7114         /* this point is not constrained */
7115         newNumPoints++;
7116         newNumIndices += bSecDof;
7117         for (f = 0; f < numFields; ++f) {
7118           PetscInt fDof;
7119 
7120           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7121           newOffsets[f + 1] += fDof;
7122         }
7123       }
7124     }
7125   }
7126   if (!anyConstrained) {
7127     if (outNumPoints) *outNumPoints = 0;
7128     if (outNumIndices) *outNumIndices = 0;
7129     if (outPoints) *outPoints = NULL;
7130     if (outValues) *outValues = NULL;
7131     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7132     PetscFunctionReturn(0);
7133   }
7134 
7135   if (outNumPoints) *outNumPoints = newNumPoints;
7136   if (outNumIndices) *outNumIndices = newNumIndices;
7137 
7138   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7139 
7140   if (!outPoints && !outValues) {
7141     if (offsets) {
7142       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7143     }
7144     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7145     PetscFunctionReturn(0);
7146   }
7147 
7148   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7149 
7150   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7151 
7152   /* workspaces */
7153   if (numFields) {
7154     for (f = 0; f < numFields; f++) {
7155       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7156       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7157     }
7158   } else {
7159     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7160     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7161   }
7162 
7163   /* get workspaces for the point-to-point matrices */
7164   if (numFields) {
7165     PetscInt totalOffset, totalMatOffset;
7166 
7167     for (p = 0; p < numPoints; p++) {
7168       PetscInt b    = points[2 * p];
7169       PetscInt bDof = 0, bSecDof;
7170 
7171       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7172       if (!bSecDof) {
7173         for (f = 0; f < numFields; f++) {
7174           newPointOffsets[f][p + 1] = 0;
7175           pointMatOffsets[f][p + 1] = 0;
7176         }
7177         continue;
7178       }
7179       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7180       if (bDof) {
7181         for (f = 0; f < numFields; f++) {
7182           PetscInt fDof, q, bOff, allFDof = 0;
7183 
7184           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7185           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7186           for (q = 0; q < bDof; q++) {
7187             PetscInt a = anchors[bOff + q];
7188             PetscInt aFDof;
7189 
7190             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7191             allFDof += aFDof;
7192           }
7193           newPointOffsets[f][p + 1] = allFDof;
7194           pointMatOffsets[f][p + 1] = fDof * allFDof;
7195         }
7196       } else {
7197         for (f = 0; f < numFields; f++) {
7198           PetscInt fDof;
7199 
7200           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7201           newPointOffsets[f][p + 1] = fDof;
7202           pointMatOffsets[f][p + 1] = 0;
7203         }
7204       }
7205     }
7206     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7207       newPointOffsets[f][0] = totalOffset;
7208       pointMatOffsets[f][0] = totalMatOffset;
7209       for (p = 0; p < numPoints; p++) {
7210         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7211         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7212       }
7213       totalOffset    = newPointOffsets[f][numPoints];
7214       totalMatOffset = pointMatOffsets[f][numPoints];
7215       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7216     }
7217   } else {
7218     for (p = 0; p < numPoints; p++) {
7219       PetscInt b    = points[2 * p];
7220       PetscInt bDof = 0, bSecDof;
7221 
7222       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7223       if (!bSecDof) {
7224         newPointOffsets[0][p + 1] = 0;
7225         pointMatOffsets[0][p + 1] = 0;
7226         continue;
7227       }
7228       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7229       if (bDof) {
7230         PetscInt bOff, q, allDof = 0;
7231 
7232         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7233         for (q = 0; q < bDof; q++) {
7234           PetscInt a = anchors[bOff + q], aDof;
7235 
7236           PetscCall(PetscSectionGetDof(section, a, &aDof));
7237           allDof += aDof;
7238         }
7239         newPointOffsets[0][p + 1] = allDof;
7240         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7241       } else {
7242         newPointOffsets[0][p + 1] = bSecDof;
7243         pointMatOffsets[0][p + 1] = 0;
7244       }
7245     }
7246     newPointOffsets[0][0] = 0;
7247     pointMatOffsets[0][0] = 0;
7248     for (p = 0; p < numPoints; p++) {
7249       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7250       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7251     }
7252     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7253   }
7254 
7255   /* output arrays */
7256   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7257 
7258   /* get the point-to-point matrices; construct newPoints */
7259   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7260   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7261   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7262   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7263   if (numFields) {
7264     for (p = 0, newP = 0; p < numPoints; p++) {
7265       PetscInt b    = points[2 * p];
7266       PetscInt o    = points[2 * p + 1];
7267       PetscInt bDof = 0, bSecDof;
7268 
7269       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7270       if (!bSecDof) continue;
7271       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7272       if (bDof) {
7273         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7274 
7275         fStart[0] = 0;
7276         fEnd[0]   = 0;
7277         for (f = 0; f < numFields; f++) {
7278           PetscInt fDof;
7279 
7280           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7281           fStart[f + 1] = fStart[f] + fDof;
7282           fEnd[f + 1]   = fStart[f + 1];
7283         }
7284         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7285         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7286 
7287         fAnchorStart[0] = 0;
7288         fAnchorEnd[0]   = 0;
7289         for (f = 0; f < numFields; f++) {
7290           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7291 
7292           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7293           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7294         }
7295         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7296         for (q = 0; q < bDof; q++) {
7297           PetscInt a = anchors[bOff + q], aOff;
7298 
7299           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7300           newPoints[2 * (newP + q)]     = a;
7301           newPoints[2 * (newP + q) + 1] = 0;
7302           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7303           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7304         }
7305         newP += bDof;
7306 
7307         if (outValues) {
7308           /* get the point-to-point submatrix */
7309           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]));
7310         }
7311       } else {
7312         newPoints[2 * newP]     = b;
7313         newPoints[2 * newP + 1] = o;
7314         newP++;
7315       }
7316     }
7317   } else {
7318     for (p = 0; p < numPoints; p++) {
7319       PetscInt b    = points[2 * p];
7320       PetscInt o    = points[2 * p + 1];
7321       PetscInt bDof = 0, bSecDof;
7322 
7323       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7324       if (!bSecDof) continue;
7325       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7326       if (bDof) {
7327         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7328 
7329         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7330         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7331 
7332         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7333         for (q = 0; q < bDof; q++) {
7334           PetscInt a = anchors[bOff + q], aOff;
7335 
7336           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7337 
7338           newPoints[2 * (newP + q)]     = a;
7339           newPoints[2 * (newP + q) + 1] = 0;
7340           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7341           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7342         }
7343         newP += bDof;
7344 
7345         /* get the point-to-point submatrix */
7346         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7347       } else {
7348         newPoints[2 * newP]     = b;
7349         newPoints[2 * newP + 1] = o;
7350         newP++;
7351       }
7352     }
7353   }
7354 
7355   if (outValues) {
7356     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7357     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7358     /* multiply constraints on the right */
7359     if (numFields) {
7360       for (f = 0; f < numFields; f++) {
7361         PetscInt oldOff = offsets[f];
7362 
7363         for (p = 0; p < numPoints; p++) {
7364           PetscInt cStart = newPointOffsets[f][p];
7365           PetscInt b      = points[2 * p];
7366           PetscInt c, r, k;
7367           PetscInt dof;
7368 
7369           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7370           if (!dof) continue;
7371           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7372             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7373             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7374 
7375             for (r = 0; r < numIndices; r++) {
7376               for (c = 0; c < nCols; c++) {
7377                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7378               }
7379             }
7380           } else {
7381             /* copy this column as is */
7382             for (r = 0; r < numIndices; r++) {
7383               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7384             }
7385           }
7386           oldOff += dof;
7387         }
7388       }
7389     } else {
7390       PetscInt oldOff = 0;
7391       for (p = 0; p < numPoints; p++) {
7392         PetscInt cStart = newPointOffsets[0][p];
7393         PetscInt b      = points[2 * p];
7394         PetscInt c, r, k;
7395         PetscInt dof;
7396 
7397         PetscCall(PetscSectionGetDof(section, b, &dof));
7398         if (!dof) continue;
7399         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7400           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7401           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7402 
7403           for (r = 0; r < numIndices; r++) {
7404             for (c = 0; c < nCols; c++) {
7405               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7406             }
7407           }
7408         } else {
7409           /* copy this column as is */
7410           for (r = 0; r < numIndices; r++) {
7411             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7412           }
7413         }
7414         oldOff += dof;
7415       }
7416     }
7417 
7418     if (multiplyLeft) {
7419       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7420       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7421       /* multiply constraints transpose on the left */
7422       if (numFields) {
7423         for (f = 0; f < numFields; f++) {
7424           PetscInt oldOff = offsets[f];
7425 
7426           for (p = 0; p < numPoints; p++) {
7427             PetscInt rStart = newPointOffsets[f][p];
7428             PetscInt b      = points[2 * p];
7429             PetscInt c, r, k;
7430             PetscInt dof;
7431 
7432             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7433             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7434               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7435               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7436 
7437               for (r = 0; r < nRows; r++) {
7438                 for (c = 0; c < newNumIndices; c++) {
7439                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7440                 }
7441               }
7442             } else {
7443               /* copy this row as is */
7444               for (r = 0; r < dof; r++) {
7445                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7446               }
7447             }
7448             oldOff += dof;
7449           }
7450         }
7451       } else {
7452         PetscInt oldOff = 0;
7453 
7454         for (p = 0; p < numPoints; p++) {
7455           PetscInt rStart = newPointOffsets[0][p];
7456           PetscInt b      = points[2 * p];
7457           PetscInt c, r, k;
7458           PetscInt dof;
7459 
7460           PetscCall(PetscSectionGetDof(section, b, &dof));
7461           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7462             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7463             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7464 
7465             for (r = 0; r < nRows; r++) {
7466               for (c = 0; c < newNumIndices; c++) {
7467                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7468               }
7469             }
7470           } else {
7471             /* copy this row as is */
7472             for (r = 0; r < dof; r++) {
7473               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7474             }
7475           }
7476           oldOff += dof;
7477         }
7478       }
7479 
7480       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7481     } else {
7482       newValues = tmpValues;
7483     }
7484   }
7485 
7486   /* clean up */
7487   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7488   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7489 
7490   if (numFields) {
7491     for (f = 0; f < numFields; f++) {
7492       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7493       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7494       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7495     }
7496   } else {
7497     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7498     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7499     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7500   }
7501   PetscCall(ISRestoreIndices(aIS, &anchors));
7502 
7503   /* output */
7504   if (outPoints) {
7505     *outPoints = newPoints;
7506   } else {
7507     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7508   }
7509   if (outValues) *outValues = newValues;
7510   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7511   PetscFunctionReturn(0);
7512 }
7513 
7514 /*@C
7515   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7516 
7517   Not collective
7518 
7519   Input Parameters:
7520 + dm         - The DM
7521 . section    - The PetscSection describing the points (a local section)
7522 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7523 . point      - The point defining the closure
7524 - useClPerm  - Use the closure point permutation if available
7525 
7526   Output Parameters:
7527 + numIndices - The number of dof indices in the closure of point with the input sections
7528 . indices    - The dof indices
7529 . outOffsets - Array to write the field offsets into, or NULL
7530 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7531 
7532   Notes:
7533   Must call DMPlexRestoreClosureIndices() to free allocated memory
7534 
7535   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7536   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7537   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7538   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7539   indices (with the above semantics) are implied.
7540 
7541   Level: advanced
7542 
7543 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7544 @*/
7545 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7546 {
7547   /* Closure ordering */
7548   PetscSection    clSection;
7549   IS              clPoints;
7550   const PetscInt *clp;
7551   PetscInt       *points;
7552   const PetscInt *clperm = NULL;
7553   /* Dof permutation and sign flips */
7554   const PetscInt    **perms[32] = {NULL};
7555   const PetscScalar **flips[32] = {NULL};
7556   PetscScalar        *valCopy   = NULL;
7557   /* Hanging node constraints */
7558   PetscInt    *pointsC = NULL;
7559   PetscScalar *valuesC = NULL;
7560   PetscInt     NclC, NiC;
7561 
7562   PetscInt *idx;
7563   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7564   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7565 
7566   PetscFunctionBeginHot;
7567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7568   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7569   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7570   if (numIndices) PetscValidIntPointer(numIndices, 6);
7571   if (indices) PetscValidPointer(indices, 7);
7572   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7573   if (values) PetscValidPointer(values, 9);
7574   PetscCall(PetscSectionGetNumFields(section, &Nf));
7575   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7576   PetscCall(PetscArrayzero(offsets, 32));
7577   /* 1) Get points in closure */
7578   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7579   if (useClPerm) {
7580     PetscInt depth, clsize;
7581     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7582     for (clsize = 0, p = 0; p < Ncl; p++) {
7583       PetscInt dof;
7584       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7585       clsize += dof;
7586     }
7587     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7588   }
7589   /* 2) Get number of indices on these points and field offsets from section */
7590   for (p = 0; p < Ncl * 2; p += 2) {
7591     PetscInt dof, fdof;
7592 
7593     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7594     for (f = 0; f < Nf; ++f) {
7595       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7596       offsets[f + 1] += fdof;
7597     }
7598     Ni += dof;
7599   }
7600   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7601   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7602   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7603   for (f = 0; f < PetscMax(1, Nf); ++f) {
7604     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7605     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7606     /* may need to apply sign changes to the element matrix */
7607     if (values && flips[f]) {
7608       PetscInt foffset = offsets[f];
7609 
7610       for (p = 0; p < Ncl; ++p) {
7611         PetscInt           pnt  = points[2 * p], fdof;
7612         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7613 
7614         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7615         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7616         if (flip) {
7617           PetscInt i, j, k;
7618 
7619           if (!valCopy) {
7620             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7621             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7622             *values = valCopy;
7623           }
7624           for (i = 0; i < fdof; ++i) {
7625             PetscScalar fval = flip[i];
7626 
7627             for (k = 0; k < Ni; ++k) {
7628               valCopy[Ni * (foffset + i) + k] *= fval;
7629               valCopy[Ni * k + (foffset + i)] *= fval;
7630             }
7631           }
7632         }
7633         foffset += fdof;
7634       }
7635     }
7636   }
7637   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7638   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7639   if (NclC) {
7640     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7641     for (f = 0; f < PetscMax(1, Nf); ++f) {
7642       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7643       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7644     }
7645     for (f = 0; f < PetscMax(1, Nf); ++f) {
7646       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7647       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7648     }
7649     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7650     Ncl    = NclC;
7651     Ni     = NiC;
7652     points = pointsC;
7653     if (values) *values = valuesC;
7654   }
7655   /* 5) Calculate indices */
7656   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7657   if (Nf) {
7658     PetscInt  idxOff;
7659     PetscBool useFieldOffsets;
7660 
7661     if (outOffsets) {
7662       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7663     }
7664     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7665     if (useFieldOffsets) {
7666       for (p = 0; p < Ncl; ++p) {
7667         const PetscInt pnt = points[p * 2];
7668 
7669         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7670       }
7671     } else {
7672       for (p = 0; p < Ncl; ++p) {
7673         const PetscInt pnt = points[p * 2];
7674 
7675         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7676         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7677          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7678          * global section. */
7679         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7680       }
7681     }
7682   } else {
7683     PetscInt off = 0, idxOff;
7684 
7685     for (p = 0; p < Ncl; ++p) {
7686       const PetscInt  pnt  = points[p * 2];
7687       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7688 
7689       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7690       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7691        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7692       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7693     }
7694   }
7695   /* 6) Cleanup */
7696   for (f = 0; f < PetscMax(1, Nf); ++f) {
7697     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7698     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7699   }
7700   if (NclC) {
7701     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7702   } else {
7703     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7704   }
7705 
7706   if (numIndices) *numIndices = Ni;
7707   if (indices) *indices = idx;
7708   PetscFunctionReturn(0);
7709 }
7710 
7711 /*@C
7712   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7713 
7714   Not collective
7715 
7716   Input Parameters:
7717 + dm         - The DM
7718 . section    - The PetscSection describing the points (a local section)
7719 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7720 . point      - The point defining the closure
7721 - useClPerm  - Use the closure point permutation if available
7722 
7723   Output Parameters:
7724 + numIndices - The number of dof indices in the closure of point with the input sections
7725 . indices    - The dof indices
7726 . outOffsets - Array to write the field offsets into, or NULL
7727 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7728 
7729   Notes:
7730   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7731 
7732   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7733   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7734   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7735   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7736   indices (with the above semantics) are implied.
7737 
7738   Level: advanced
7739 
7740 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7741 @*/
7742 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7743 {
7744   PetscFunctionBegin;
7745   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7746   PetscValidPointer(indices, 7);
7747   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7748   PetscFunctionReturn(0);
7749 }
7750 
7751 /*@C
7752   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7753 
7754   Not collective
7755 
7756   Input Parameters:
7757 + dm - The DM
7758 . section - The section describing the layout in v, or NULL to use the default section
7759 . globalSection - The section describing the layout in v, or NULL to use the default global section
7760 . A - The matrix
7761 . point - The point in the DM
7762 . values - The array of values
7763 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7764 
7765   Fortran Notes:
7766   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7767 
7768   Level: intermediate
7769 
7770 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7771 @*/
7772 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7773 {
7774   DM_Plex           *mesh = (DM_Plex *)dm->data;
7775   PetscInt          *indices;
7776   PetscInt           numIndices;
7777   const PetscScalar *valuesOrig = values;
7778   PetscErrorCode     ierr;
7779 
7780   PetscFunctionBegin;
7781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7782   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7783   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7784   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7785   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7786   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7787 
7788   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7789 
7790   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7791   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7792   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7793   if (ierr) {
7794     PetscMPIInt rank;
7795 
7796     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7797     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7798     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7799     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7800     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7801     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7802   }
7803   if (mesh->printFEM > 1) {
7804     PetscInt i;
7805     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7806     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7807     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7808   }
7809 
7810   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7811   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7812   PetscFunctionReturn(0);
7813 }
7814 
7815 /*@C
7816   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7817 
7818   Not collective
7819 
7820   Input Parameters:
7821 + dmRow - The DM for the row fields
7822 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7823 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7824 . dmCol - The DM for the column fields
7825 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7826 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7827 . A - The matrix
7828 . point - The point in the DMs
7829 . values - The array of values
7830 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7831 
7832   Level: intermediate
7833 
7834 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7835 @*/
7836 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7837 {
7838   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7839   PetscInt          *indicesRow, *indicesCol;
7840   PetscInt           numIndicesRow, numIndicesCol;
7841   const PetscScalar *valuesOrig = values;
7842   PetscErrorCode     ierr;
7843 
7844   PetscFunctionBegin;
7845   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7846   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7847   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7848   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7849   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7850   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7851   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7852   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7853   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7854   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7855   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7856 
7857   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7858   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7859 
7860   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7861   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7862   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7863   if (ierr) {
7864     PetscMPIInt rank;
7865 
7866     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7867     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7868     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7869     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7870     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7871     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7872   }
7873 
7874   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7875   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7876   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7877   PetscFunctionReturn(0);
7878 }
7879 
7880 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7881 {
7882   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7883   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7884   PetscInt       *cpoints = NULL;
7885   PetscInt       *findices, *cindices;
7886   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7887   PetscInt        foffsets[32], coffsets[32];
7888   DMPolytopeType  ct;
7889   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7890   PetscErrorCode  ierr;
7891 
7892   PetscFunctionBegin;
7893   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7894   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7895   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7896   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7897   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7898   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7899   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7900   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7901   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7902   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7903   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7904   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7905   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7906   PetscCall(PetscArrayzero(foffsets, 32));
7907   PetscCall(PetscArrayzero(coffsets, 32));
7908   /* Column indices */
7909   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7910   maxFPoints = numCPoints;
7911   /* Compress out points not in the section */
7912   /*   TODO: Squeeze out points with 0 dof as well */
7913   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7914   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7915     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7916       cpoints[q * 2]     = cpoints[p];
7917       cpoints[q * 2 + 1] = cpoints[p + 1];
7918       ++q;
7919     }
7920   }
7921   numCPoints = q;
7922   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7923     PetscInt fdof;
7924 
7925     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7926     if (!dof) continue;
7927     for (f = 0; f < numFields; ++f) {
7928       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7929       coffsets[f + 1] += fdof;
7930     }
7931     numCIndices += dof;
7932   }
7933   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7934   /* Row indices */
7935   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7936   {
7937     DMPlexTransform tr;
7938     DMPolytopeType *rct;
7939     PetscInt       *rsize, *rcone, *rornt, Nt;
7940 
7941     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7942     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7943     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7944     numSubcells = rsize[Nt - 1];
7945     PetscCall(DMPlexTransformDestroy(&tr));
7946   }
7947   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7948   for (r = 0, q = 0; r < numSubcells; ++r) {
7949     /* TODO Map from coarse to fine cells */
7950     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7951     /* Compress out points not in the section */
7952     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7953     for (p = 0; p < numFPoints * 2; p += 2) {
7954       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7955         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7956         if (!dof) continue;
7957         for (s = 0; s < q; ++s)
7958           if (fpoints[p] == ftotpoints[s * 2]) break;
7959         if (s < q) continue;
7960         ftotpoints[q * 2]     = fpoints[p];
7961         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7962         ++q;
7963       }
7964     }
7965     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7966   }
7967   numFPoints = q;
7968   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7969     PetscInt fdof;
7970 
7971     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7972     if (!dof) continue;
7973     for (f = 0; f < numFields; ++f) {
7974       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7975       foffsets[f + 1] += fdof;
7976     }
7977     numFIndices += dof;
7978   }
7979   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7980 
7981   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7982   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7983   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7984   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7985   if (numFields) {
7986     const PetscInt **permsF[32] = {NULL};
7987     const PetscInt **permsC[32] = {NULL};
7988 
7989     for (f = 0; f < numFields; f++) {
7990       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7991       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7992     }
7993     for (p = 0; p < numFPoints; p++) {
7994       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7995       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7996     }
7997     for (p = 0; p < numCPoints; p++) {
7998       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7999       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8000     }
8001     for (f = 0; f < numFields; f++) {
8002       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8003       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8004     }
8005   } else {
8006     const PetscInt **permsF = NULL;
8007     const PetscInt **permsC = NULL;
8008 
8009     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8010     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8011     for (p = 0, off = 0; p < numFPoints; p++) {
8012       const PetscInt *perm = permsF ? permsF[p] : NULL;
8013 
8014       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8015       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8016     }
8017     for (p = 0, off = 0; p < numCPoints; p++) {
8018       const PetscInt *perm = permsC ? permsC[p] : NULL;
8019 
8020       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8021       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8022     }
8023     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8024     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8025   }
8026   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8027   /* TODO: flips */
8028   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8029   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8030   if (ierr) {
8031     PetscMPIInt rank;
8032 
8033     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8034     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8035     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8036     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8037     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8038   }
8039   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8040   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8041   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8042   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8043   PetscFunctionReturn(0);
8044 }
8045 
8046 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8047 {
8048   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8049   PetscInt       *cpoints = NULL;
8050   PetscInt        foffsets[32], coffsets[32];
8051   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8052   DMPolytopeType  ct;
8053   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8054 
8055   PetscFunctionBegin;
8056   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8057   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8058   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8059   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8060   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8061   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8062   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8063   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8064   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8065   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8066   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8067   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8068   PetscCall(PetscArrayzero(foffsets, 32));
8069   PetscCall(PetscArrayzero(coffsets, 32));
8070   /* Column indices */
8071   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8072   maxFPoints = numCPoints;
8073   /* Compress out points not in the section */
8074   /*   TODO: Squeeze out points with 0 dof as well */
8075   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8076   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8077     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8078       cpoints[q * 2]     = cpoints[p];
8079       cpoints[q * 2 + 1] = cpoints[p + 1];
8080       ++q;
8081     }
8082   }
8083   numCPoints = q;
8084   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8085     PetscInt fdof;
8086 
8087     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8088     if (!dof) continue;
8089     for (f = 0; f < numFields; ++f) {
8090       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8091       coffsets[f + 1] += fdof;
8092     }
8093     numCIndices += dof;
8094   }
8095   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8096   /* Row indices */
8097   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8098   {
8099     DMPlexTransform tr;
8100     DMPolytopeType *rct;
8101     PetscInt       *rsize, *rcone, *rornt, Nt;
8102 
8103     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8104     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8105     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8106     numSubcells = rsize[Nt - 1];
8107     PetscCall(DMPlexTransformDestroy(&tr));
8108   }
8109   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8110   for (r = 0, q = 0; r < numSubcells; ++r) {
8111     /* TODO Map from coarse to fine cells */
8112     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8113     /* Compress out points not in the section */
8114     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8115     for (p = 0; p < numFPoints * 2; p += 2) {
8116       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8117         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8118         if (!dof) continue;
8119         for (s = 0; s < q; ++s)
8120           if (fpoints[p] == ftotpoints[s * 2]) break;
8121         if (s < q) continue;
8122         ftotpoints[q * 2]     = fpoints[p];
8123         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8124         ++q;
8125       }
8126     }
8127     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8128   }
8129   numFPoints = q;
8130   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8131     PetscInt fdof;
8132 
8133     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8134     if (!dof) continue;
8135     for (f = 0; f < numFields; ++f) {
8136       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8137       foffsets[f + 1] += fdof;
8138     }
8139     numFIndices += dof;
8140   }
8141   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8142 
8143   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8144   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8145   if (numFields) {
8146     const PetscInt **permsF[32] = {NULL};
8147     const PetscInt **permsC[32] = {NULL};
8148 
8149     for (f = 0; f < numFields; f++) {
8150       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8151       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8152     }
8153     for (p = 0; p < numFPoints; p++) {
8154       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8155       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8156     }
8157     for (p = 0; p < numCPoints; p++) {
8158       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8159       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8160     }
8161     for (f = 0; f < numFields; f++) {
8162       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8163       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8164     }
8165   } else {
8166     const PetscInt **permsF = NULL;
8167     const PetscInt **permsC = NULL;
8168 
8169     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8170     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8171     for (p = 0, off = 0; p < numFPoints; p++) {
8172       const PetscInt *perm = permsF ? permsF[p] : NULL;
8173 
8174       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8175       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8176     }
8177     for (p = 0, off = 0; p < numCPoints; p++) {
8178       const PetscInt *perm = permsC ? permsC[p] : NULL;
8179 
8180       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8181       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8182     }
8183     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8184     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8185   }
8186   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8187   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8188   PetscFunctionReturn(0);
8189 }
8190 
8191 /*@C
8192   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8193 
8194   Input Parameter:
8195 . dm   - The DMPlex object
8196 
8197   Output Parameter:
8198 . cellHeight - The height of a cell
8199 
8200   Level: developer
8201 
8202 .seealso `DMPlexSetVTKCellHeight()`
8203 @*/
8204 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8205 {
8206   DM_Plex *mesh = (DM_Plex *)dm->data;
8207 
8208   PetscFunctionBegin;
8209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8210   PetscValidIntPointer(cellHeight, 2);
8211   *cellHeight = mesh->vtkCellHeight;
8212   PetscFunctionReturn(0);
8213 }
8214 
8215 /*@C
8216   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8217 
8218   Input Parameters:
8219 + dm   - The DMPlex object
8220 - cellHeight - The height of a cell
8221 
8222   Level: developer
8223 
8224 .seealso `DMPlexGetVTKCellHeight()`
8225 @*/
8226 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8227 {
8228   DM_Plex *mesh = (DM_Plex *)dm->data;
8229 
8230   PetscFunctionBegin;
8231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8232   mesh->vtkCellHeight = cellHeight;
8233   PetscFunctionReturn(0);
8234 }
8235 
8236 /*@
8237   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8238 
8239   Input Parameter:
8240 . dm - The DMPlex object
8241 
8242   Output Parameters:
8243 + gcStart - The first ghost cell, or NULL
8244 - gcEnd   - The upper bound on ghost cells, or NULL
8245 
8246   Level: advanced
8247 
8248 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8249 @*/
8250 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8251 {
8252   DMLabel ctLabel;
8253 
8254   PetscFunctionBegin;
8255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8256   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8257   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8258   // Reset label for fast lookup
8259   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8260   PetscFunctionReturn(0);
8261 }
8262 
8263 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8264 {
8265   PetscSection section, globalSection;
8266   PetscInt    *numbers, p;
8267 
8268   PetscFunctionBegin;
8269   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8270   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8271   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8272   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8273   PetscCall(PetscSectionSetUp(section));
8274   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8275   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8276   for (p = pStart; p < pEnd; ++p) {
8277     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8278     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8279     else numbers[p - pStart] += shift;
8280   }
8281   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8282   if (globalSize) {
8283     PetscLayout layout;
8284     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8285     PetscCall(PetscLayoutGetSize(layout, globalSize));
8286     PetscCall(PetscLayoutDestroy(&layout));
8287   }
8288   PetscCall(PetscSectionDestroy(&section));
8289   PetscCall(PetscSectionDestroy(&globalSection));
8290   PetscFunctionReturn(0);
8291 }
8292 
8293 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8294 {
8295   PetscInt cellHeight, cStart, cEnd;
8296 
8297   PetscFunctionBegin;
8298   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8299   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8300   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8301   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8302   PetscFunctionReturn(0);
8303 }
8304 
8305 /*@
8306   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8307 
8308   Input Parameter:
8309 . dm   - The DMPlex object
8310 
8311   Output Parameter:
8312 . globalCellNumbers - Global cell numbers for all cells on this process
8313 
8314   Level: developer
8315 
8316 .seealso `DMPlexGetVertexNumbering()`
8317 @*/
8318 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8319 {
8320   DM_Plex *mesh = (DM_Plex *)dm->data;
8321 
8322   PetscFunctionBegin;
8323   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8324   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8325   *globalCellNumbers = mesh->globalCellNumbers;
8326   PetscFunctionReturn(0);
8327 }
8328 
8329 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8330 {
8331   PetscInt vStart, vEnd;
8332 
8333   PetscFunctionBegin;
8334   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8335   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8336   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8337   PetscFunctionReturn(0);
8338 }
8339 
8340 /*@
8341   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8342 
8343   Input Parameter:
8344 . dm   - The DMPlex object
8345 
8346   Output Parameter:
8347 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8348 
8349   Level: developer
8350 
8351 .seealso `DMPlexGetCellNumbering()`
8352 @*/
8353 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8354 {
8355   DM_Plex *mesh = (DM_Plex *)dm->data;
8356 
8357   PetscFunctionBegin;
8358   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8359   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8360   *globalVertexNumbers = mesh->globalVertexNumbers;
8361   PetscFunctionReturn(0);
8362 }
8363 
8364 /*@
8365   DMPlexCreatePointNumbering - Create a global numbering for all points.
8366 
8367   Collective on dm
8368 
8369   Input Parameter:
8370 . dm   - The DMPlex object
8371 
8372   Output Parameter:
8373 . globalPointNumbers - Global numbers for all points on this process
8374 
8375   Notes:
8376 
8377   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8378   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8379   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8380   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8381 
8382   The partitioned mesh is
8383 ```
8384  (2)--0--(3)--1--(4)    (1)--0--(2)
8385 ```
8386   and its global numbering is
8387 ```
8388   (3)--0--(4)--1--(5)--2--(6)
8389 ```
8390   Then the global numbering is provided as
8391 ```
8392 [0] Number of indices in set 5
8393 [0] 0 0
8394 [0] 1 1
8395 [0] 2 3
8396 [0] 3 4
8397 [0] 4 -6
8398 [1] Number of indices in set 3
8399 [1] 0 2
8400 [1] 1 5
8401 [1] 2 6
8402 ```
8403 
8404   Level: developer
8405 
8406 .seealso `DMPlexGetCellNumbering()`
8407 @*/
8408 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8409 {
8410   IS        nums[4];
8411   PetscInt  depths[4], gdepths[4], starts[4];
8412   PetscInt  depth, d, shift = 0;
8413   PetscBool empty = PETSC_FALSE;
8414 
8415   PetscFunctionBegin;
8416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8417   PetscCall(DMPlexGetDepth(dm, &depth));
8418   // For unstratified meshes use dim instead of depth
8419   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8420   // If any stratum is empty, we must mark all empty
8421   for (d = 0; d <= depth; ++d) {
8422     PetscInt end;
8423 
8424     depths[d] = depth - d;
8425     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8426     if (!(starts[d] - end)) empty = PETSC_TRUE;
8427   }
8428   if (empty)
8429     for (d = 0; d <= depth; ++d) {
8430       depths[d] = -1;
8431       starts[d] = -1;
8432     }
8433   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8434   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8435   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]);
8436   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8437   for (d = 0; d <= depth; ++d) {
8438     PetscInt pStart, pEnd, gsize;
8439 
8440     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8441     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8442     shift += gsize;
8443   }
8444   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8445   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8446   PetscFunctionReturn(0);
8447 }
8448 
8449 /*@
8450   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8451 
8452   Input Parameter:
8453 . dm - The DMPlex object
8454 
8455   Output Parameter:
8456 . ranks - The rank field
8457 
8458   Options Database Keys:
8459 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8460 
8461   Level: intermediate
8462 
8463 .seealso: `DMView()`
8464 @*/
8465 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8466 {
8467   DM             rdm;
8468   PetscFE        fe;
8469   PetscScalar   *r;
8470   PetscMPIInt    rank;
8471   DMPolytopeType ct;
8472   PetscInt       dim, cStart, cEnd, c;
8473   PetscBool      simplex;
8474 
8475   PetscFunctionBeginUser;
8476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8477   PetscValidPointer(ranks, 2);
8478   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8479   PetscCall(DMClone(dm, &rdm));
8480   PetscCall(DMGetDimension(rdm, &dim));
8481   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8482   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8483   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8484   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8485   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8486   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8487   PetscCall(PetscFEDestroy(&fe));
8488   PetscCall(DMCreateDS(rdm));
8489   PetscCall(DMCreateGlobalVector(rdm, ranks));
8490   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8491   PetscCall(VecGetArray(*ranks, &r));
8492   for (c = cStart; c < cEnd; ++c) {
8493     PetscScalar *lr;
8494 
8495     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8496     if (lr) *lr = rank;
8497   }
8498   PetscCall(VecRestoreArray(*ranks, &r));
8499   PetscCall(DMDestroy(&rdm));
8500   PetscFunctionReturn(0);
8501 }
8502 
8503 /*@
8504   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8505 
8506   Input Parameters:
8507 + dm    - The DMPlex
8508 - label - The DMLabel
8509 
8510   Output Parameter:
8511 . val - The label value field
8512 
8513   Options Database Keys:
8514 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8515 
8516   Level: intermediate
8517 
8518 .seealso: `DMView()`
8519 @*/
8520 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8521 {
8522   DM           rdm;
8523   PetscFE      fe;
8524   PetscScalar *v;
8525   PetscInt     dim, cStart, cEnd, c;
8526 
8527   PetscFunctionBeginUser;
8528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8529   PetscValidPointer(label, 2);
8530   PetscValidPointer(val, 3);
8531   PetscCall(DMClone(dm, &rdm));
8532   PetscCall(DMGetDimension(rdm, &dim));
8533   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8534   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8535   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8536   PetscCall(PetscFEDestroy(&fe));
8537   PetscCall(DMCreateDS(rdm));
8538   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8539   PetscCall(DMCreateGlobalVector(rdm, val));
8540   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8541   PetscCall(VecGetArray(*val, &v));
8542   for (c = cStart; c < cEnd; ++c) {
8543     PetscScalar *lv;
8544     PetscInt     cval;
8545 
8546     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8547     PetscCall(DMLabelGetValue(label, c, &cval));
8548     *lv = cval;
8549   }
8550   PetscCall(VecRestoreArray(*val, &v));
8551   PetscCall(DMDestroy(&rdm));
8552   PetscFunctionReturn(0);
8553 }
8554 
8555 /*@
8556   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8557 
8558   Input Parameter:
8559 . dm - The DMPlex object
8560 
8561   Notes:
8562   This is a useful diagnostic when creating meshes programmatically.
8563 
8564   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8565 
8566   Level: developer
8567 
8568 .seealso: `DMCreate()`, `DMSetFromOptions()`
8569 @*/
8570 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8571 {
8572   PetscSection    coneSection, supportSection;
8573   const PetscInt *cone, *support;
8574   PetscInt        coneSize, c, supportSize, s;
8575   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8576   PetscBool       storagecheck = PETSC_TRUE;
8577 
8578   PetscFunctionBegin;
8579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8580   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8581   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8582   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8583   /* Check that point p is found in the support of its cone points, and vice versa */
8584   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8585   for (p = pStart; p < pEnd; ++p) {
8586     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8587     PetscCall(DMPlexGetCone(dm, p, &cone));
8588     for (c = 0; c < coneSize; ++c) {
8589       PetscBool dup = PETSC_FALSE;
8590       PetscInt  d;
8591       for (d = c - 1; d >= 0; --d) {
8592         if (cone[c] == cone[d]) {
8593           dup = PETSC_TRUE;
8594           break;
8595         }
8596       }
8597       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8598       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8599       for (s = 0; s < supportSize; ++s) {
8600         if (support[s] == p) break;
8601       }
8602       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8603         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8604         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8605         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8606         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8607         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8608         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8609         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]);
8610         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8611       }
8612     }
8613     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8614     if (p != pp) {
8615       storagecheck = PETSC_FALSE;
8616       continue;
8617     }
8618     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8619     PetscCall(DMPlexGetSupport(dm, p, &support));
8620     for (s = 0; s < supportSize; ++s) {
8621       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8622       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8623       for (c = 0; c < coneSize; ++c) {
8624         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8625         if (cone[c] != pp) {
8626           c = 0;
8627           break;
8628         }
8629         if (cone[c] == p) break;
8630       }
8631       if (c >= coneSize) {
8632         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8633         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8634         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8635         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8636         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8637         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8638         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8639       }
8640     }
8641   }
8642   if (storagecheck) {
8643     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8644     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8645     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8646   }
8647   PetscFunctionReturn(0);
8648 }
8649 
8650 /*
8651   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.
8652 */
8653 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8654 {
8655   DMPolytopeType  cct;
8656   PetscInt        ptpoints[4];
8657   const PetscInt *cone, *ccone, *ptcone;
8658   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8659 
8660   PetscFunctionBegin;
8661   *unsplit = 0;
8662   switch (ct) {
8663   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8664     ptpoints[npt++] = c;
8665     break;
8666   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8667     PetscCall(DMPlexGetCone(dm, c, &cone));
8668     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8669     for (cp = 0; cp < coneSize; ++cp) {
8670       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8671       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8672     }
8673     break;
8674   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8675   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8676     PetscCall(DMPlexGetCone(dm, c, &cone));
8677     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8678     for (cp = 0; cp < coneSize; ++cp) {
8679       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8680       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8681       for (ccp = 0; ccp < cconeSize; ++ccp) {
8682         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8683         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8684           PetscInt p;
8685           for (p = 0; p < npt; ++p)
8686             if (ptpoints[p] == ccone[ccp]) break;
8687           if (p == npt) ptpoints[npt++] = ccone[ccp];
8688         }
8689       }
8690     }
8691     break;
8692   default:
8693     break;
8694   }
8695   for (pt = 0; pt < npt; ++pt) {
8696     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8697     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8698   }
8699   PetscFunctionReturn(0);
8700 }
8701 
8702 /*@
8703   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8704 
8705   Input Parameters:
8706 + dm - The DMPlex object
8707 - cellHeight - Normally 0
8708 
8709   Notes:
8710   This is a useful diagnostic when creating meshes programmatically.
8711   Currently applicable only to homogeneous simplex or tensor meshes.
8712 
8713   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8714 
8715   Level: developer
8716 
8717 .seealso: `DMCreate()`, `DMSetFromOptions()`
8718 @*/
8719 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8720 {
8721   DMPlexInterpolatedFlag interp;
8722   DMPolytopeType         ct;
8723   PetscInt               vStart, vEnd, cStart, cEnd, c;
8724 
8725   PetscFunctionBegin;
8726   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8727   PetscCall(DMPlexIsInterpolated(dm, &interp));
8728   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8729   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8730   for (c = cStart; c < cEnd; ++c) {
8731     PetscInt *closure = NULL;
8732     PetscInt  coneSize, closureSize, cl, Nv = 0;
8733 
8734     PetscCall(DMPlexGetCellType(dm, c, &ct));
8735     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8736     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8737     if (interp == DMPLEX_INTERPOLATED_FULL) {
8738       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8739       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));
8740     }
8741     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8742     for (cl = 0; cl < closureSize * 2; cl += 2) {
8743       const PetscInt p = closure[cl];
8744       if ((p >= vStart) && (p < vEnd)) ++Nv;
8745     }
8746     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8747     /* Special Case: Tensor faces with identified vertices */
8748     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8749       PetscInt unsplit;
8750 
8751       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8752       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8753     }
8754     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));
8755   }
8756   PetscFunctionReturn(0);
8757 }
8758 
8759 /*@
8760   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8761 
8762   Collective
8763 
8764   Input Parameters:
8765 + dm - The DMPlex object
8766 - cellHeight - Normally 0
8767 
8768   Notes:
8769   This is a useful diagnostic when creating meshes programmatically.
8770   This routine is only relevant for meshes that are fully interpolated across all ranks.
8771   It will error out if a partially interpolated mesh is given on some rank.
8772   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8773 
8774   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8775 
8776   Level: developer
8777 
8778 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8779 @*/
8780 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8781 {
8782   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8783   DMPlexInterpolatedFlag interpEnum;
8784 
8785   PetscFunctionBegin;
8786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8787   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8788   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8789   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8790     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8791     PetscFunctionReturn(0);
8792   }
8793 
8794   PetscCall(DMGetDimension(dm, &dim));
8795   PetscCall(DMPlexGetDepth(dm, &depth));
8796   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8797   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8798     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8799     for (c = cStart; c < cEnd; ++c) {
8800       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8801       const DMPolytopeType *faceTypes;
8802       DMPolytopeType        ct;
8803       PetscInt              numFaces, coneSize, f;
8804       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8805 
8806       PetscCall(DMPlexGetCellType(dm, c, &ct));
8807       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8808       if (unsplit) continue;
8809       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8810       PetscCall(DMPlexGetCone(dm, c, &cone));
8811       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8812       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8813       for (cl = 0; cl < closureSize * 2; cl += 2) {
8814         const PetscInt p = closure[cl];
8815         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8816       }
8817       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8818       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);
8819       for (f = 0; f < numFaces; ++f) {
8820         DMPolytopeType fct;
8821         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8822 
8823         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8824         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8825         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8826           const PetscInt p = fclosure[cl];
8827           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8828         }
8829         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]);
8830         for (v = 0; v < fnumCorners; ++v) {
8831           if (fclosure[v] != faces[fOff + v]) {
8832             PetscInt v1;
8833 
8834             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8835             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8836             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8837             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8838             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8839             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]);
8840           }
8841         }
8842         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8843         fOff += faceSizes[f];
8844       }
8845       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8846       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8847     }
8848   }
8849   PetscFunctionReturn(0);
8850 }
8851 
8852 /*@
8853   DMPlexCheckGeometry - Check the geometry of mesh cells
8854 
8855   Input Parameter:
8856 . dm - The DMPlex object
8857 
8858   Notes:
8859   This is a useful diagnostic when creating meshes programmatically.
8860 
8861   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8862 
8863   Level: developer
8864 
8865 .seealso: `DMCreate()`, `DMSetFromOptions()`
8866 @*/
8867 PetscErrorCode DMPlexCheckGeometry(DM dm)
8868 {
8869   Vec       coordinates;
8870   PetscReal detJ, J[9], refVol = 1.0;
8871   PetscReal vol;
8872   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8873 
8874   PetscFunctionBegin;
8875   PetscCall(DMGetDimension(dm, &dim));
8876   PetscCall(DMGetCoordinateDim(dm, &dE));
8877   if (dim != dE) PetscFunctionReturn(0);
8878   PetscCall(DMPlexGetDepth(dm, &depth));
8879   for (d = 0; d < dim; ++d) refVol *= 2.0;
8880   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8881   /* Make sure local coordinates are created, because that step is collective */
8882   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8883   if (!coordinates) PetscFunctionReturn(0);
8884   for (c = cStart; c < cEnd; ++c) {
8885     DMPolytopeType ct;
8886     PetscInt       unsplit;
8887     PetscBool      ignoreZeroVol = PETSC_FALSE;
8888 
8889     PetscCall(DMPlexGetCellType(dm, c, &ct));
8890     switch (ct) {
8891     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8892     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8893     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8894       ignoreZeroVol = PETSC_TRUE;
8895       break;
8896     default:
8897       break;
8898     }
8899     switch (ct) {
8900     case DM_POLYTOPE_TRI_PRISM:
8901     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8902     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8903     case DM_POLYTOPE_PYRAMID:
8904       continue;
8905     default:
8906       break;
8907     }
8908     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8909     if (unsplit) continue;
8910     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8911     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);
8912     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8913     /* This should work with periodicity since DG coordinates should be used */
8914     if (depth > 1) {
8915       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8916       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);
8917       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8918     }
8919   }
8920   PetscFunctionReturn(0);
8921 }
8922 
8923 /*@
8924   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8925 
8926   Collective
8927 
8928   Input Parameters:
8929 + dm - The DMPlex object
8930 . pointSF - The Point SF, or NULL for Point SF attached to DM
8931 - allowExtraRoots - Flag to allow extra points not present in the DM
8932 
8933   Notes:
8934   This is mainly intended for debugging/testing purposes.
8935 
8936   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8937 
8938   Extra roots can come from priodic cuts, where additional points appear on the boundary
8939 
8940   Level: developer
8941 
8942 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8943 @*/
8944 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8945 {
8946   PetscInt           l, nleaves, nroots, overlap;
8947   const PetscInt    *locals;
8948   const PetscSFNode *remotes;
8949   PetscBool          distributed;
8950   MPI_Comm           comm;
8951   PetscMPIInt        rank;
8952 
8953   PetscFunctionBegin;
8954   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8955   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8956   else pointSF = dm->sf;
8957   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8958   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8959   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8960   {
8961     PetscMPIInt mpiFlag;
8962 
8963     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8964     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8965   }
8966   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8967   PetscCall(DMPlexIsDistributed(dm, &distributed));
8968   if (!distributed) {
8969     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);
8970     PetscFunctionReturn(0);
8971   }
8972   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);
8973   PetscCall(DMPlexGetOverlap(dm, &overlap));
8974 
8975   /* Check SF graph is compatible with DMPlex chart */
8976   {
8977     PetscInt pStart, pEnd, maxLeaf;
8978 
8979     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8980     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8981     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8982     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8983   }
8984 
8985   /* Check Point SF has no local points referenced */
8986   for (l = 0; l < nleaves; l++) {
8987     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);
8988   }
8989 
8990   /* Check there are no cells in interface */
8991   if (!overlap) {
8992     PetscInt cellHeight, cStart, cEnd;
8993 
8994     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8995     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8996     for (l = 0; l < nleaves; ++l) {
8997       const PetscInt point = locals ? locals[l] : l;
8998 
8999       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9000     }
9001   }
9002 
9003   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9004   {
9005     const PetscInt *rootdegree;
9006 
9007     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9008     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9009     for (l = 0; l < nleaves; ++l) {
9010       const PetscInt  point = locals ? locals[l] : l;
9011       const PetscInt *cone;
9012       PetscInt        coneSize, c, idx;
9013 
9014       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9015       PetscCall(DMPlexGetCone(dm, point, &cone));
9016       for (c = 0; c < coneSize; ++c) {
9017         if (!rootdegree[cone[c]]) {
9018           if (locals) {
9019             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9020           } else {
9021             idx = (cone[c] < nleaves) ? cone[c] : -1;
9022           }
9023           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9024         }
9025       }
9026     }
9027   }
9028   PetscFunctionReturn(0);
9029 }
9030 
9031 /*@
9032   DMPlexCheck - Perform various checks of Plex sanity
9033 
9034   Input Parameter:
9035 . dm - The DMPlex object
9036 
9037   Notes:
9038   This is a useful diagnostic when creating meshes programmatically.
9039 
9040   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9041 
9042   Currently does not include DMPlexCheckCellShape().
9043 
9044   Level: developer
9045 
9046 .seealso: DMCreate(), DMSetFromOptions()
9047 @*/
9048 PetscErrorCode DMPlexCheck(DM dm)
9049 {
9050   PetscInt cellHeight;
9051 
9052   PetscFunctionBegin;
9053   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9054   PetscCall(DMPlexCheckSymmetry(dm));
9055   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9056   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9057   PetscCall(DMPlexCheckGeometry(dm));
9058   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9059   PetscCall(DMPlexCheckInterfaceCones(dm));
9060   PetscFunctionReturn(0);
9061 }
9062 
9063 typedef struct cell_stats {
9064   PetscReal min, max, sum, squaresum;
9065   PetscInt  count;
9066 } cell_stats_t;
9067 
9068 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9069 {
9070   PetscInt i, N = *len;
9071 
9072   for (i = 0; i < N; i++) {
9073     cell_stats_t *A = (cell_stats_t *)a;
9074     cell_stats_t *B = (cell_stats_t *)b;
9075 
9076     B->min = PetscMin(A->min, B->min);
9077     B->max = PetscMax(A->max, B->max);
9078     B->sum += A->sum;
9079     B->squaresum += A->squaresum;
9080     B->count += A->count;
9081   }
9082 }
9083 
9084 /*@
9085   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9086 
9087   Collective on dm
9088 
9089   Input Parameters:
9090 + dm        - The DMPlex object
9091 . output    - If true, statistics will be displayed on stdout
9092 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
9093 
9094   Notes:
9095   This is mainly intended for debugging/testing purposes.
9096 
9097   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9098 
9099   Level: developer
9100 
9101 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9102 @*/
9103 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9104 {
9105   DM           dmCoarse;
9106   cell_stats_t stats, globalStats;
9107   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9108   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9109   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9110   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9111   PetscMPIInt  rank, size;
9112 
9113   PetscFunctionBegin;
9114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9115   stats.min = PETSC_MAX_REAL;
9116   stats.max = PETSC_MIN_REAL;
9117   stats.sum = stats.squaresum = 0.;
9118   stats.count                 = 0;
9119 
9120   PetscCallMPI(MPI_Comm_size(comm, &size));
9121   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9122   PetscCall(DMGetCoordinateDim(dm, &cdim));
9123   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9124   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9125   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9126   for (c = cStart; c < cEnd; c++) {
9127     PetscInt  i;
9128     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9129 
9130     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9131     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9132     for (i = 0; i < PetscSqr(cdim); ++i) {
9133       frobJ += J[i] * J[i];
9134       frobInvJ += invJ[i] * invJ[i];
9135     }
9136     cond2 = frobJ * frobInvJ;
9137     cond  = PetscSqrtReal(cond2);
9138 
9139     stats.min = PetscMin(stats.min, cond);
9140     stats.max = PetscMax(stats.max, cond);
9141     stats.sum += cond;
9142     stats.squaresum += cond2;
9143     stats.count++;
9144     if (output && cond > limit) {
9145       PetscSection coordSection;
9146       Vec          coordsLocal;
9147       PetscScalar *coords = NULL;
9148       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9149 
9150       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9151       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9152       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9153       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9154       for (i = 0; i < Nv / cdim; ++i) {
9155         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9156         for (d = 0; d < cdim; ++d) {
9157           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9158           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9159         }
9160         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9161       }
9162       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9163       for (cl = 0; cl < clSize * 2; cl += 2) {
9164         const PetscInt edge = closure[cl];
9165 
9166         if ((edge >= eStart) && (edge < eEnd)) {
9167           PetscReal len;
9168 
9169           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9170           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9171         }
9172       }
9173       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9174       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9175     }
9176   }
9177   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9178 
9179   if (size > 1) {
9180     PetscMPIInt  blockLengths[2] = {4, 1};
9181     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9182     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9183     MPI_Op       statReduce;
9184 
9185     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9186     PetscCallMPI(MPI_Type_commit(&statType));
9187     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9188     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9189     PetscCallMPI(MPI_Op_free(&statReduce));
9190     PetscCallMPI(MPI_Type_free(&statType));
9191   } else {
9192     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9193   }
9194   if (rank == 0) {
9195     count = globalStats.count;
9196     min   = globalStats.min;
9197     max   = globalStats.max;
9198     mean  = globalStats.sum / globalStats.count;
9199     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9200   }
9201 
9202   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));
9203   PetscCall(PetscFree2(J, invJ));
9204 
9205   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9206   if (dmCoarse) {
9207     PetscBool isplex;
9208 
9209     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9210     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9211   }
9212   PetscFunctionReturn(0);
9213 }
9214 
9215 /*@
9216   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9217   orthogonal quality below given tolerance.
9218 
9219   Collective on dm
9220 
9221   Input Parameters:
9222 + dm   - The DMPlex object
9223 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
9224 - atol - [0, 1] Absolute tolerance for tagging cells.
9225 
9226   Output Parameters:
9227 + OrthQual      - Vec containing orthogonal quality per cell
9228 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
9229 
9230   Options Database Keys:
9231 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
9232 supported.
9233 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9234 
9235   Notes:
9236   Orthogonal quality is given by the following formula:
9237 
9238   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
9239 
9240   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
9241   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9242   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9243   calculating the cosine of the angle between these vectors.
9244 
9245   Orthogonal quality ranges from 1 (best) to 0 (worst).
9246 
9247   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
9248   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9249 
9250   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9251 
9252   Level: intermediate
9253 
9254 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
9255 @*/
9256 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9257 {
9258   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9259   PetscInt              *idx;
9260   PetscScalar           *oqVals;
9261   const PetscScalar     *cellGeomArr, *faceGeomArr;
9262   PetscReal             *ci, *fi, *Ai;
9263   MPI_Comm               comm;
9264   Vec                    cellgeom, facegeom;
9265   DM                     dmFace, dmCell;
9266   IS                     glob;
9267   ISLocalToGlobalMapping ltog;
9268   PetscViewer            vwr;
9269 
9270   PetscFunctionBegin;
9271   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9272   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9273   PetscValidPointer(OrthQual, 4);
9274   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9275   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9276   PetscCall(DMGetDimension(dm, &nc));
9277   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9278   {
9279     DMPlexInterpolatedFlag interpFlag;
9280 
9281     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9282     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9283       PetscMPIInt rank;
9284 
9285       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9286       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9287     }
9288   }
9289   if (OrthQualLabel) {
9290     PetscValidPointer(OrthQualLabel, 5);
9291     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9292     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9293   } else {
9294     *OrthQualLabel = NULL;
9295   }
9296   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9297   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9298   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9299   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9300   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9301   PetscCall(VecCreate(comm, OrthQual));
9302   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9303   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9304   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9305   PetscCall(VecSetUp(*OrthQual));
9306   PetscCall(ISDestroy(&glob));
9307   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9308   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9309   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9310   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9311   PetscCall(VecGetDM(cellgeom, &dmCell));
9312   PetscCall(VecGetDM(facegeom, &dmFace));
9313   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9314   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9315     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9316     PetscInt         cellarr[2], *adj = NULL;
9317     PetscScalar     *cArr, *fArr;
9318     PetscReal        minvalc = 1.0, minvalf = 1.0;
9319     PetscFVCellGeom *cg;
9320 
9321     idx[cellIter] = cell - cStart;
9322     cellarr[0]    = cell;
9323     /* Make indexing into cellGeom easier */
9324     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9325     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9326     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9327     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9328     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9329       PetscInt         i;
9330       const PetscInt   neigh  = adj[cellneigh];
9331       PetscReal        normci = 0, normfi = 0, normai = 0;
9332       PetscFVCellGeom *cgneigh;
9333       PetscFVFaceGeom *fg;
9334 
9335       /* Don't count ourselves in the neighbor list */
9336       if (neigh == cell) continue;
9337       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9338       cellarr[1] = neigh;
9339       {
9340         PetscInt        numcovpts;
9341         const PetscInt *covpts;
9342 
9343         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9344         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9345         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9346       }
9347 
9348       /* Compute c_i, f_i and their norms */
9349       for (i = 0; i < nc; i++) {
9350         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9351         fi[i] = fg->centroid[i] - cg->centroid[i];
9352         Ai[i] = fg->normal[i];
9353         normci += PetscPowReal(ci[i], 2);
9354         normfi += PetscPowReal(fi[i], 2);
9355         normai += PetscPowReal(Ai[i], 2);
9356       }
9357       normci = PetscSqrtReal(normci);
9358       normfi = PetscSqrtReal(normfi);
9359       normai = PetscSqrtReal(normai);
9360 
9361       /* Normalize and compute for each face-cell-normal pair */
9362       for (i = 0; i < nc; i++) {
9363         ci[i] = ci[i] / normci;
9364         fi[i] = fi[i] / normfi;
9365         Ai[i] = Ai[i] / normai;
9366         /* PetscAbs because I don't know if normals are guaranteed to point out */
9367         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9368         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9369       }
9370       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9371       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9372     }
9373     PetscCall(PetscFree(adj));
9374     PetscCall(PetscFree2(cArr, fArr));
9375     /* Defer to cell if they're equal */
9376     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9377     if (OrthQualLabel) {
9378       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9379     }
9380   }
9381   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9382   PetscCall(VecAssemblyBegin(*OrthQual));
9383   PetscCall(VecAssemblyEnd(*OrthQual));
9384   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9385   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9386   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9387   if (OrthQualLabel) {
9388     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9389   }
9390   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9391   PetscCall(PetscViewerDestroy(&vwr));
9392   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9393   PetscFunctionReturn(0);
9394 }
9395 
9396 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9397  * interpolator construction */
9398 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9399 {
9400   PetscSection section, newSection, gsection;
9401   PetscSF      sf;
9402   PetscBool    hasConstraints, ghasConstraints;
9403 
9404   PetscFunctionBegin;
9405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9406   PetscValidPointer(odm, 2);
9407   PetscCall(DMGetLocalSection(dm, &section));
9408   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9409   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9410   if (!ghasConstraints) {
9411     PetscCall(PetscObjectReference((PetscObject)dm));
9412     *odm = dm;
9413     PetscFunctionReturn(0);
9414   }
9415   PetscCall(DMClone(dm, odm));
9416   PetscCall(DMCopyFields(dm, *odm));
9417   PetscCall(DMGetLocalSection(*odm, &newSection));
9418   PetscCall(DMGetPointSF(*odm, &sf));
9419   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9420   PetscCall(DMSetGlobalSection(*odm, gsection));
9421   PetscCall(PetscSectionDestroy(&gsection));
9422   PetscFunctionReturn(0);
9423 }
9424 
9425 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9426 {
9427   DM        dmco, dmfo;
9428   Mat       interpo;
9429   Vec       rscale;
9430   Vec       cglobalo, clocal;
9431   Vec       fglobal, fglobalo, flocal;
9432   PetscBool regular;
9433 
9434   PetscFunctionBegin;
9435   PetscCall(DMGetFullDM(dmc, &dmco));
9436   PetscCall(DMGetFullDM(dmf, &dmfo));
9437   PetscCall(DMSetCoarseDM(dmfo, dmco));
9438   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9439   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9440   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9441   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9442   PetscCall(DMCreateLocalVector(dmc, &clocal));
9443   PetscCall(VecSet(cglobalo, 0.));
9444   PetscCall(VecSet(clocal, 0.));
9445   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9446   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9447   PetscCall(DMCreateLocalVector(dmf, &flocal));
9448   PetscCall(VecSet(fglobal, 0.));
9449   PetscCall(VecSet(fglobalo, 0.));
9450   PetscCall(VecSet(flocal, 0.));
9451   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9452   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9453   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9454   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9455   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9456   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9457   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9458   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9459   *shift = fglobal;
9460   PetscCall(VecDestroy(&flocal));
9461   PetscCall(VecDestroy(&fglobalo));
9462   PetscCall(VecDestroy(&clocal));
9463   PetscCall(VecDestroy(&cglobalo));
9464   PetscCall(VecDestroy(&rscale));
9465   PetscCall(MatDestroy(&interpo));
9466   PetscCall(DMDestroy(&dmfo));
9467   PetscCall(DMDestroy(&dmco));
9468   PetscFunctionReturn(0);
9469 }
9470 
9471 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9472 {
9473   PetscObject shifto;
9474   Vec         shift;
9475 
9476   PetscFunctionBegin;
9477   if (!interp) {
9478     Vec rscale;
9479 
9480     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9481     PetscCall(VecDestroy(&rscale));
9482   } else {
9483     PetscCall(PetscObjectReference((PetscObject)interp));
9484   }
9485   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9486   if (!shifto) {
9487     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9488     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9489     shifto = (PetscObject)shift;
9490     PetscCall(VecDestroy(&shift));
9491   }
9492   shift = (Vec)shifto;
9493   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9494   PetscCall(VecAXPY(fineSol, 1.0, shift));
9495   PetscCall(MatDestroy(&interp));
9496   PetscFunctionReturn(0);
9497 }
9498 
9499 /* Pointwise interpolation
9500      Just code FEM for now
9501      u^f = I u^c
9502      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9503      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9504      I_{ij} = psi^f_i phi^c_j
9505 */
9506 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9507 {
9508   PetscSection gsc, gsf;
9509   PetscInt     m, n;
9510   void        *ctx;
9511   DM           cdm;
9512   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9513 
9514   PetscFunctionBegin;
9515   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9516   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9517   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9518   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9519 
9520   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9521   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9522   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9523   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9524   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9525 
9526   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9527   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9528   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9529   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9530   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9531   if (scaling) {
9532     /* Use naive scaling */
9533     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9534   }
9535   PetscFunctionReturn(0);
9536 }
9537 
9538 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9539 {
9540   VecScatter ctx;
9541 
9542   PetscFunctionBegin;
9543   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9544   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9545   PetscCall(VecScatterDestroy(&ctx));
9546   PetscFunctionReturn(0);
9547 }
9548 
9549 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[])
9550 {
9551   const PetscInt Nc = uOff[1] - uOff[0];
9552   PetscInt       c;
9553   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9554 }
9555 
9556 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9557 {
9558   DM           dmc;
9559   PetscDS      ds;
9560   Vec          ones, locmass;
9561   IS           cellIS;
9562   PetscFormKey key;
9563   PetscInt     depth;
9564 
9565   PetscFunctionBegin;
9566   PetscCall(DMClone(dm, &dmc));
9567   PetscCall(DMCopyDisc(dm, dmc));
9568   PetscCall(DMGetDS(dmc, &ds));
9569   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9570   PetscCall(DMCreateGlobalVector(dmc, mass));
9571   PetscCall(DMGetLocalVector(dmc, &ones));
9572   PetscCall(DMGetLocalVector(dmc, &locmass));
9573   PetscCall(DMPlexGetDepth(dmc, &depth));
9574   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9575   PetscCall(VecSet(locmass, 0.0));
9576   PetscCall(VecSet(ones, 1.0));
9577   key.label = NULL;
9578   key.value = 0;
9579   key.field = 0;
9580   key.part  = 0;
9581   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9582   PetscCall(ISDestroy(&cellIS));
9583   PetscCall(VecSet(*mass, 0.0));
9584   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9585   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9586   PetscCall(DMRestoreLocalVector(dmc, &ones));
9587   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9588   PetscCall(DMDestroy(&dmc));
9589   PetscFunctionReturn(0);
9590 }
9591 
9592 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9593 {
9594   PetscSection gsc, gsf;
9595   PetscInt     m, n;
9596   void        *ctx;
9597   DM           cdm;
9598   PetscBool    regular;
9599 
9600   PetscFunctionBegin;
9601   if (dmFine == dmCoarse) {
9602     DM            dmc;
9603     PetscDS       ds;
9604     PetscWeakForm wf;
9605     Vec           u;
9606     IS            cellIS;
9607     PetscFormKey  key;
9608     PetscInt      depth;
9609 
9610     PetscCall(DMClone(dmFine, &dmc));
9611     PetscCall(DMCopyDisc(dmFine, dmc));
9612     PetscCall(DMGetDS(dmc, &ds));
9613     PetscCall(PetscDSGetWeakForm(ds, &wf));
9614     PetscCall(PetscWeakFormClear(wf));
9615     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9616     PetscCall(DMCreateMatrix(dmc, mass));
9617     PetscCall(DMGetLocalVector(dmc, &u));
9618     PetscCall(DMPlexGetDepth(dmc, &depth));
9619     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9620     PetscCall(MatZeroEntries(*mass));
9621     key.label = NULL;
9622     key.value = 0;
9623     key.field = 0;
9624     key.part  = 0;
9625     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9626     PetscCall(ISDestroy(&cellIS));
9627     PetscCall(DMRestoreLocalVector(dmc, &u));
9628     PetscCall(DMDestroy(&dmc));
9629   } else {
9630     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9631     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9632     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9633     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9634 
9635     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9636     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9637     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9638     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9639 
9640     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9641     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9642     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9643     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9644   }
9645   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9646   PetscFunctionReturn(0);
9647 }
9648 
9649 /*@
9650   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9651 
9652   Input Parameter:
9653 . dm - The DMPlex object
9654 
9655   Output Parameter:
9656 . regular - The flag
9657 
9658   Level: intermediate
9659 
9660 .seealso: `DMPlexSetRegularRefinement()`
9661 @*/
9662 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9663 {
9664   PetscFunctionBegin;
9665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9666   PetscValidBoolPointer(regular, 2);
9667   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9668   PetscFunctionReturn(0);
9669 }
9670 
9671 /*@
9672   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9673 
9674   Input Parameters:
9675 + dm - The DMPlex object
9676 - regular - The flag
9677 
9678   Level: intermediate
9679 
9680 .seealso: `DMPlexGetRegularRefinement()`
9681 @*/
9682 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9683 {
9684   PetscFunctionBegin;
9685   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9686   ((DM_Plex *)dm->data)->regularRefinement = regular;
9687   PetscFunctionReturn(0);
9688 }
9689 
9690 /* anchors */
9691 /*@
9692   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9693   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9694 
9695   not collective
9696 
9697   Input Parameter:
9698 . dm - The DMPlex object
9699 
9700   Output Parameters:
9701 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9702 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9703 
9704   Level: intermediate
9705 
9706 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9707 @*/
9708 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9709 {
9710   DM_Plex *plex = (DM_Plex *)dm->data;
9711 
9712   PetscFunctionBegin;
9713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9714   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9715   if (anchorSection) *anchorSection = plex->anchorSection;
9716   if (anchorIS) *anchorIS = plex->anchorIS;
9717   PetscFunctionReturn(0);
9718 }
9719 
9720 /*@
9721   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9722   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9723   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9724 
9725   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9726   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9727 
9728   collective on dm
9729 
9730   Input Parameters:
9731 + dm - The DMPlex object
9732 . 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).
9733 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9734 
9735   The reference counts of anchorSection and anchorIS are incremented.
9736 
9737   Level: intermediate
9738 
9739 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9740 @*/
9741 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9742 {
9743   DM_Plex    *plex = (DM_Plex *)dm->data;
9744   PetscMPIInt result;
9745 
9746   PetscFunctionBegin;
9747   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9748   if (anchorSection) {
9749     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9750     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9751     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9752   }
9753   if (anchorIS) {
9754     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9755     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9756     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9757   }
9758 
9759   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9760   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9761   plex->anchorSection = anchorSection;
9762 
9763   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9764   PetscCall(ISDestroy(&plex->anchorIS));
9765   plex->anchorIS = anchorIS;
9766 
9767   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9768     PetscInt        size, a, pStart, pEnd;
9769     const PetscInt *anchors;
9770 
9771     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9772     PetscCall(ISGetLocalSize(anchorIS, &size));
9773     PetscCall(ISGetIndices(anchorIS, &anchors));
9774     for (a = 0; a < size; a++) {
9775       PetscInt p;
9776 
9777       p = anchors[a];
9778       if (p >= pStart && p < pEnd) {
9779         PetscInt dof;
9780 
9781         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9782         if (dof) {
9783           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9784           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9785         }
9786       }
9787     }
9788     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9789   }
9790   /* reset the generic constraints */
9791   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9792   PetscFunctionReturn(0);
9793 }
9794 
9795 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9796 {
9797   PetscSection anchorSection;
9798   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9799 
9800   PetscFunctionBegin;
9801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9802   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9803   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9804   PetscCall(PetscSectionGetNumFields(section, &numFields));
9805   if (numFields) {
9806     PetscInt f;
9807     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9808 
9809     for (f = 0; f < numFields; f++) {
9810       PetscInt numComp;
9811 
9812       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9813       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9814     }
9815   }
9816   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9817   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9818   pStart = PetscMax(pStart, sStart);
9819   pEnd   = PetscMin(pEnd, sEnd);
9820   pEnd   = PetscMax(pStart, pEnd);
9821   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9822   for (p = pStart; p < pEnd; p++) {
9823     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9824     if (dof) {
9825       PetscCall(PetscSectionGetDof(section, p, &dof));
9826       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9827       for (f = 0; f < numFields; f++) {
9828         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9829         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9830       }
9831     }
9832   }
9833   PetscCall(PetscSectionSetUp(*cSec));
9834   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9835   PetscFunctionReturn(0);
9836 }
9837 
9838 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9839 {
9840   PetscSection    aSec;
9841   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9842   const PetscInt *anchors;
9843   PetscInt        numFields, f;
9844   IS              aIS;
9845   MatType         mtype;
9846   PetscBool       iscuda, iskokkos;
9847 
9848   PetscFunctionBegin;
9849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9850   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9851   PetscCall(PetscSectionGetStorageSize(section, &n));
9852   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9853   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9854   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9855   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9856   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9857   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9858   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9859   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9860   else mtype = MATSEQAIJ;
9861   PetscCall(MatSetType(*cMat, mtype));
9862   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9863   PetscCall(ISGetIndices(aIS, &anchors));
9864   /* cSec will be a subset of aSec and section */
9865   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9866   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9867   PetscCall(PetscMalloc1(m + 1, &i));
9868   i[0] = 0;
9869   PetscCall(PetscSectionGetNumFields(section, &numFields));
9870   for (p = pStart; p < pEnd; p++) {
9871     PetscInt rDof, rOff, r;
9872 
9873     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9874     if (!rDof) continue;
9875     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9876     if (numFields) {
9877       for (f = 0; f < numFields; f++) {
9878         annz = 0;
9879         for (r = 0; r < rDof; r++) {
9880           a = anchors[rOff + r];
9881           if (a < sStart || a >= sEnd) continue;
9882           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9883           annz += aDof;
9884         }
9885         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9886         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9887         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9888       }
9889     } else {
9890       annz = 0;
9891       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9892       for (q = 0; q < dof; q++) {
9893         a = anchors[rOff + q];
9894         if (a < sStart || a >= sEnd) continue;
9895         PetscCall(PetscSectionGetDof(section, a, &aDof));
9896         annz += aDof;
9897       }
9898       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9899       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9900       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9901     }
9902   }
9903   nnz = i[m];
9904   PetscCall(PetscMalloc1(nnz, &j));
9905   offset = 0;
9906   for (p = pStart; p < pEnd; p++) {
9907     if (numFields) {
9908       for (f = 0; f < numFields; f++) {
9909         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9910         for (q = 0; q < dof; q++) {
9911           PetscInt rDof, rOff, r;
9912           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9913           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9914           for (r = 0; r < rDof; r++) {
9915             PetscInt s;
9916 
9917             a = anchors[rOff + r];
9918             if (a < sStart || a >= sEnd) continue;
9919             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9920             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9921             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9922           }
9923         }
9924       }
9925     } else {
9926       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9927       for (q = 0; q < dof; q++) {
9928         PetscInt rDof, rOff, r;
9929         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9930         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9931         for (r = 0; r < rDof; r++) {
9932           PetscInt s;
9933 
9934           a = anchors[rOff + r];
9935           if (a < sStart || a >= sEnd) continue;
9936           PetscCall(PetscSectionGetDof(section, a, &aDof));
9937           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9938           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9939         }
9940       }
9941     }
9942   }
9943   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9944   PetscCall(PetscFree(i));
9945   PetscCall(PetscFree(j));
9946   PetscCall(ISRestoreIndices(aIS, &anchors));
9947   PetscFunctionReturn(0);
9948 }
9949 
9950 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9951 {
9952   DM_Plex     *plex = (DM_Plex *)dm->data;
9953   PetscSection anchorSection, section, cSec;
9954   Mat          cMat;
9955 
9956   PetscFunctionBegin;
9957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9958   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9959   if (anchorSection) {
9960     PetscInt Nf;
9961 
9962     PetscCall(DMGetLocalSection(dm, &section));
9963     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9964     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9965     PetscCall(DMGetNumFields(dm, &Nf));
9966     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9967     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9968     PetscCall(PetscSectionDestroy(&cSec));
9969     PetscCall(MatDestroy(&cMat));
9970   }
9971   PetscFunctionReturn(0);
9972 }
9973 
9974 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9975 {
9976   IS           subis;
9977   PetscSection section, subsection;
9978 
9979   PetscFunctionBegin;
9980   PetscCall(DMGetLocalSection(dm, &section));
9981   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9982   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9983   /* Create subdomain */
9984   PetscCall(DMPlexFilter(dm, label, value, subdm));
9985   /* Create submodel */
9986   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9987   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9988   PetscCall(DMSetLocalSection(*subdm, subsection));
9989   PetscCall(PetscSectionDestroy(&subsection));
9990   PetscCall(DMCopyDisc(dm, *subdm));
9991   /* Create map from submodel to global model */
9992   if (is) {
9993     PetscSection    sectionGlobal, subsectionGlobal;
9994     IS              spIS;
9995     const PetscInt *spmap;
9996     PetscInt       *subIndices;
9997     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9998     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9999 
10000     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10001     PetscCall(ISGetIndices(spIS, &spmap));
10002     PetscCall(PetscSectionGetNumFields(section, &Nf));
10003     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10004     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10005     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10006     for (p = pStart; p < pEnd; ++p) {
10007       PetscInt gdof, pSubSize = 0;
10008 
10009       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10010       if (gdof > 0) {
10011         for (f = 0; f < Nf; ++f) {
10012           PetscInt fdof, fcdof;
10013 
10014           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10015           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10016           pSubSize += fdof - fcdof;
10017         }
10018         subSize += pSubSize;
10019         if (pSubSize) {
10020           if (bs < 0) {
10021             bs = pSubSize;
10022           } else if (bs != pSubSize) {
10023             /* Layout does not admit a pointwise block size */
10024             bs = 1;
10025           }
10026         }
10027       }
10028     }
10029     /* Must have same blocksize on all procs (some might have no points) */
10030     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10031     bsLocal[1] = bs;
10032     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10033     if (bsMinMax[0] != bsMinMax[1]) {
10034       bs = 1;
10035     } else {
10036       bs = bsMinMax[0];
10037     }
10038     PetscCall(PetscMalloc1(subSize, &subIndices));
10039     for (p = pStart; p < pEnd; ++p) {
10040       PetscInt gdof, goff;
10041 
10042       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10043       if (gdof > 0) {
10044         const PetscInt point = spmap[p];
10045 
10046         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10047         for (f = 0; f < Nf; ++f) {
10048           PetscInt fdof, fcdof, fc, f2, poff = 0;
10049 
10050           /* Can get rid of this loop by storing field information in the global section */
10051           for (f2 = 0; f2 < f; ++f2) {
10052             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10053             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10054             poff += fdof - fcdof;
10055           }
10056           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10057           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10058           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10059         }
10060       }
10061     }
10062     PetscCall(ISRestoreIndices(spIS, &spmap));
10063     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10064     if (bs > 1) {
10065       /* We need to check that the block size does not come from non-contiguous fields */
10066       PetscInt i, j, set = 1;
10067       for (i = 0; i < subSize; i += bs) {
10068         for (j = 0; j < bs; ++j) {
10069           if (subIndices[i + j] != subIndices[i] + j) {
10070             set = 0;
10071             break;
10072           }
10073         }
10074       }
10075       if (set) PetscCall(ISSetBlockSize(*is, bs));
10076     }
10077     /* Attach nullspace */
10078     for (f = 0; f < Nf; ++f) {
10079       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10080       if ((*subdm)->nullspaceConstructors[f]) break;
10081     }
10082     if (f < Nf) {
10083       MatNullSpace nullSpace;
10084       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10085 
10086       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10087       PetscCall(MatNullSpaceDestroy(&nullSpace));
10088     }
10089   }
10090   PetscFunctionReturn(0);
10091 }
10092 
10093 /*@
10094   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10095 
10096   Input Parameter:
10097 - dm - The DM
10098 
10099   Level: developer
10100 
10101   Options Database Keys:
10102 . -dm_plex_monitor_throughput - Activate the monitor
10103 
10104 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
10105 @*/
10106 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10107 {
10108 #if defined(PETSC_USE_LOG)
10109   PetscStageLog      stageLog;
10110   PetscLogEvent      event;
10111   PetscLogStage      stage;
10112   PetscEventPerfInfo eventInfo;
10113   PetscReal          cellRate, flopRate;
10114   PetscInt           cStart, cEnd, Nf, N;
10115   const char        *name;
10116 #endif
10117 
10118   PetscFunctionBegin;
10119   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10120 #if defined(PETSC_USE_LOG)
10121   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10122   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10123   PetscCall(DMGetNumFields(dm, &Nf));
10124   PetscCall(PetscLogGetStageLog(&stageLog));
10125   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10126   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10127   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10128   N        = (cEnd - cStart) * Nf * eventInfo.count;
10129   flopRate = eventInfo.flops / eventInfo.time;
10130   cellRate = N / eventInfo.time;
10131   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)));
10132 #else
10133   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10134 #endif
10135   PetscFunctionReturn(0);
10136 }
10137