xref: /petsc/src/dm/impls/plex/plex.c (revision a207d08edab748df642306d213e6e891ba48ee92)
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 
5206   PetscFunctionBegin;
5207   PetscCall(DMClone(dm, cdm));
5208   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5209   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5210   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5211   PetscCall(DMSetLocalSection(*cdm, section));
5212   PetscCall(PetscSectionDestroy(&section));
5213   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5214   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5215   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5216   PetscCall(PetscSectionDestroy(&s));
5217   PetscCall(MatDestroy(&m));
5218 
5219   PetscCall(DMSetNumFields(*cdm, 1));
5220   PetscCall(DMCreateDS(*cdm));
5221   PetscFunctionReturn(0);
5222 }
5223 
5224 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5225 {
5226   Vec coordsLocal, cellCoordsLocal;
5227   DM  coordsDM, cellCoordsDM;
5228 
5229   PetscFunctionBegin;
5230   *field = NULL;
5231   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5232   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5233   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5234   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5235   if (coordsLocal && coordsDM) {
5236     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5237     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5238   }
5239   PetscFunctionReturn(0);
5240 }
5241 
5242 /*@C
5243   DMPlexGetConeSection - Return a section which describes the layout of cone data
5244 
5245   Not Collective
5246 
5247   Input Parameters:
5248 . dm        - The DMPlex object
5249 
5250   Output Parameter:
5251 . section - The PetscSection object
5252 
5253   Level: developer
5254 
5255 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5256 @*/
5257 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5258 {
5259   DM_Plex *mesh = (DM_Plex *)dm->data;
5260 
5261   PetscFunctionBegin;
5262   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5263   if (section) *section = mesh->coneSection;
5264   PetscFunctionReturn(0);
5265 }
5266 
5267 /*@C
5268   DMPlexGetSupportSection - Return a section which describes the layout of support data
5269 
5270   Not Collective
5271 
5272   Input Parameters:
5273 . dm        - The DMPlex object
5274 
5275   Output Parameter:
5276 . section - The PetscSection object
5277 
5278   Level: developer
5279 
5280 .seealso: `DMPlexGetConeSection()`
5281 @*/
5282 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5283 {
5284   DM_Plex *mesh = (DM_Plex *)dm->data;
5285 
5286   PetscFunctionBegin;
5287   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5288   if (section) *section = mesh->supportSection;
5289   PetscFunctionReturn(0);
5290 }
5291 
5292 /*@C
5293   DMPlexGetCones - Return cone data
5294 
5295   Not Collective
5296 
5297   Input Parameters:
5298 . dm        - The DMPlex object
5299 
5300   Output Parameter:
5301 . cones - The cone for each point
5302 
5303   Level: developer
5304 
5305 .seealso: `DMPlexGetConeSection()`
5306 @*/
5307 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5308 {
5309   DM_Plex *mesh = (DM_Plex *)dm->data;
5310 
5311   PetscFunctionBegin;
5312   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5313   if (cones) *cones = mesh->cones;
5314   PetscFunctionReturn(0);
5315 }
5316 
5317 /*@C
5318   DMPlexGetConeOrientations - Return cone orientation data
5319 
5320   Not Collective
5321 
5322   Input Parameters:
5323 . dm        - The DMPlex object
5324 
5325   Output Parameter:
5326 . coneOrientations - The array of cone orientations for all points
5327 
5328   Level: developer
5329 
5330   Notes:
5331   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5332 
5333   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5334 
5335 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5336 @*/
5337 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5338 {
5339   DM_Plex *mesh = (DM_Plex *)dm->data;
5340 
5341   PetscFunctionBegin;
5342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5343   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5344   PetscFunctionReturn(0);
5345 }
5346 
5347 /******************************** FEM Support **********************************/
5348 
5349 /*
5350  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5351  representing a line in the section.
5352 */
5353 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5354 {
5355   PetscFunctionBeginHot;
5356   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5357   if (line < 0) {
5358     *k  = 0;
5359     *Nc = 0;
5360   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5361     *k = 1;
5362   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5363     /* An order k SEM disc has k-1 dofs on an edge */
5364     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5365     *k = *k / *Nc + 1;
5366   }
5367   PetscFunctionReturn(0);
5368 }
5369 
5370 /*@
5371 
5372   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5373   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5374   section provided (or the section of the DM).
5375 
5376   Input Parameters:
5377 + dm      - The DM
5378 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5379 - section - The PetscSection to reorder, or NULL for the default section
5380 
5381   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5382   degree of the basis.
5383 
5384   Example:
5385   A typical interpolated single-quad mesh might order points as
5386 .vb
5387   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5388 
5389   v4 -- e6 -- v3
5390   |           |
5391   e7    c0    e8
5392   |           |
5393   v1 -- e5 -- v2
5394 .ve
5395 
5396   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5397   dofs in the order of points, e.g.,
5398 .vb
5399     c0 -> [0,1,2,3]
5400     v1 -> [4]
5401     ...
5402     e5 -> [8, 9]
5403 .ve
5404 
5405   which corresponds to the dofs
5406 .vb
5407     6   10  11  7
5408     13  2   3   15
5409     12  0   1   14
5410     4   8   9   5
5411 .ve
5412 
5413   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5414 .vb
5415   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5416 .ve
5417 
5418   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5419 .vb
5420    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5421 .ve
5422 
5423   Level: developer
5424 
5425 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5426 @*/
5427 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5428 {
5429   DMLabel   label;
5430   PetscInt  dim, depth = -1, eStart = -1, Nf;
5431   PetscBool vertexchart;
5432 
5433   PetscFunctionBegin;
5434   PetscCall(DMGetDimension(dm, &dim));
5435   if (dim < 1) PetscFunctionReturn(0);
5436   if (point < 0) {
5437     PetscInt sStart, sEnd;
5438 
5439     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5440     point = sEnd - sStart ? sStart : point;
5441   }
5442   PetscCall(DMPlexGetDepthLabel(dm, &label));
5443   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5444   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5445   if (depth == 1) {
5446     eStart = point;
5447   } else if (depth == dim) {
5448     const PetscInt *cone;
5449 
5450     PetscCall(DMPlexGetCone(dm, point, &cone));
5451     if (dim == 2) eStart = cone[0];
5452     else if (dim == 3) {
5453       const PetscInt *cone2;
5454       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5455       eStart = cone2[0];
5456     } 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);
5457   } 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);
5458   { /* Determine whether the chart covers all points or just vertices. */
5459     PetscInt pStart, pEnd, cStart, cEnd;
5460     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5461     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5462     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5463     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5464     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5465   }
5466   PetscCall(PetscSectionGetNumFields(section, &Nf));
5467   for (PetscInt d = 1; d <= dim; d++) {
5468     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5469     PetscInt *perm;
5470 
5471     for (f = 0; f < Nf; ++f) {
5472       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5473       size += PetscPowInt(k + 1, d) * Nc;
5474     }
5475     PetscCall(PetscMalloc1(size, &perm));
5476     for (f = 0; f < Nf; ++f) {
5477       switch (d) {
5478       case 1:
5479         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5480         /*
5481          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5482          We want              [ vtx0; edge of length k-1; vtx1 ]
5483          */
5484         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5485         for (i = 0; i < k - 1; i++)
5486           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5487         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5488         foffset = offset;
5489         break;
5490       case 2:
5491         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5492         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5493         /* The SEM order is
5494 
5495          v_lb, {e_b}, v_rb,
5496          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5497          v_lt, reverse {e_t}, v_rt
5498          */
5499         {
5500           const PetscInt of   = 0;
5501           const PetscInt oeb  = of + PetscSqr(k - 1);
5502           const PetscInt oer  = oeb + (k - 1);
5503           const PetscInt oet  = oer + (k - 1);
5504           const PetscInt oel  = oet + (k - 1);
5505           const PetscInt ovlb = oel + (k - 1);
5506           const PetscInt ovrb = ovlb + 1;
5507           const PetscInt ovrt = ovrb + 1;
5508           const PetscInt ovlt = ovrt + 1;
5509           PetscInt       o;
5510 
5511           /* bottom */
5512           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5513           for (o = oeb; o < oer; ++o)
5514             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5515           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5516           /* middle */
5517           for (i = 0; i < k - 1; ++i) {
5518             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5519             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++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] = (oer + i) * Nc + c + foffset;
5522           }
5523           /* top */
5524           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5525           for (o = oel - 1; o >= oet; --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] = ovrt * Nc + c + foffset;
5528           foffset = offset;
5529         }
5530         break;
5531       case 3:
5532         /* The original hex closure is
5533 
5534          {c,
5535          f_b, f_t, f_f, f_b, f_r, f_l,
5536          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5537          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5538          */
5539         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5540         /* The SEM order is
5541          Bottom Slice
5542          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5543          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5544          v_blb, {e_bb}, v_brb,
5545 
5546          Middle Slice (j)
5547          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5548          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5549          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5550 
5551          Top Slice
5552          v_tlf, {e_tf}, v_trf,
5553          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5554          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5555          */
5556         {
5557           const PetscInt oc    = 0;
5558           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5559           const PetscInt oft   = ofb + PetscSqr(k - 1);
5560           const PetscInt off   = oft + PetscSqr(k - 1);
5561           const PetscInt ofk   = off + PetscSqr(k - 1);
5562           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5563           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5564           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5565           const PetscInt oebb  = oebl + (k - 1);
5566           const PetscInt oebr  = oebb + (k - 1);
5567           const PetscInt oebf  = oebr + (k - 1);
5568           const PetscInt oetf  = oebf + (k - 1);
5569           const PetscInt oetr  = oetf + (k - 1);
5570           const PetscInt oetb  = oetr + (k - 1);
5571           const PetscInt oetl  = oetb + (k - 1);
5572           const PetscInt oerf  = oetl + (k - 1);
5573           const PetscInt oelf  = oerf + (k - 1);
5574           const PetscInt oelb  = oelf + (k - 1);
5575           const PetscInt oerb  = oelb + (k - 1);
5576           const PetscInt ovblf = oerb + (k - 1);
5577           const PetscInt ovblb = ovblf + 1;
5578           const PetscInt ovbrb = ovblb + 1;
5579           const PetscInt ovbrf = ovbrb + 1;
5580           const PetscInt ovtlf = ovbrf + 1;
5581           const PetscInt ovtrf = ovtlf + 1;
5582           const PetscInt ovtrb = ovtrf + 1;
5583           const PetscInt ovtlb = ovtrb + 1;
5584           PetscInt       o, n;
5585 
5586           /* Bottom Slice */
5587           /*   bottom */
5588           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5589           for (o = oetf - 1; o >= oebf; --o)
5590             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5591           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5592           /*   middle */
5593           for (i = 0; i < k - 1; ++i) {
5594             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5595             for (n = 0; n < k - 1; ++n) {
5596               o = ofb + n * (k - 1) + i;
5597               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5598             }
5599             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5600           }
5601           /*   top */
5602           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5603           for (o = oebb; o < oebr; ++o)
5604             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5605           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5606 
5607           /* Middle Slice */
5608           for (j = 0; j < k - 1; ++j) {
5609             /*   bottom */
5610             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5611             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5612               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5613             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5614             /*   middle */
5615             for (i = 0; i < k - 1; ++i) {
5616               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5617               for (n = 0; n < k - 1; ++n)
5618                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5619               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5620             }
5621             /*   top */
5622             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5623             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5624               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5625             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5626           }
5627 
5628           /* Top Slice */
5629           /*   bottom */
5630           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5631           for (o = oetf; o < oetr; ++o)
5632             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5633           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5634           /*   middle */
5635           for (i = 0; i < k - 1; ++i) {
5636             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5637             for (n = 0; n < k - 1; ++n)
5638               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5639             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5640           }
5641           /*   top */
5642           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5643           for (o = oetl - 1; o >= oetb; --o)
5644             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5645           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5646 
5647           foffset = offset;
5648         }
5649         break;
5650       default:
5651         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5652       }
5653     }
5654     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5655     /* Check permutation */
5656     {
5657       PetscInt *check;
5658 
5659       PetscCall(PetscMalloc1(size, &check));
5660       for (i = 0; i < size; ++i) {
5661         check[i] = -1;
5662         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5663       }
5664       for (i = 0; i < size; ++i) check[perm[i]] = i;
5665       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5666       PetscCall(PetscFree(check));
5667     }
5668     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5669     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5670       PetscInt *loc_perm;
5671       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5672       for (PetscInt i = 0; i < size; i++) {
5673         loc_perm[i]        = perm[i];
5674         loc_perm[size + i] = size + perm[i];
5675       }
5676       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5677     }
5678   }
5679   PetscFunctionReturn(0);
5680 }
5681 
5682 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5683 {
5684   PetscDS  prob;
5685   PetscInt depth, Nf, h;
5686   DMLabel  label;
5687 
5688   PetscFunctionBeginHot;
5689   PetscCall(DMGetDS(dm, &prob));
5690   Nf      = prob->Nf;
5691   label   = dm->depthLabel;
5692   *dspace = NULL;
5693   if (field < Nf) {
5694     PetscObject disc = prob->disc[field];
5695 
5696     if (disc->classid == PETSCFE_CLASSID) {
5697       PetscDualSpace dsp;
5698 
5699       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5700       PetscCall(DMLabelGetNumValues(label, &depth));
5701       PetscCall(DMLabelGetValue(label, point, &h));
5702       h = depth - 1 - h;
5703       if (h) {
5704         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5705       } else {
5706         *dspace = dsp;
5707       }
5708     }
5709   }
5710   PetscFunctionReturn(0);
5711 }
5712 
5713 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5714 {
5715   PetscScalar       *array;
5716   const PetscScalar *vArray;
5717   const PetscInt    *cone, *coneO;
5718   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5719 
5720   PetscFunctionBeginHot;
5721   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5722   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5723   PetscCall(DMPlexGetCone(dm, point, &cone));
5724   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5725   if (!values || !*values) {
5726     if ((point >= pStart) && (point < pEnd)) {
5727       PetscInt dof;
5728 
5729       PetscCall(PetscSectionGetDof(section, point, &dof));
5730       size += dof;
5731     }
5732     for (p = 0; p < numPoints; ++p) {
5733       const PetscInt cp = cone[p];
5734       PetscInt       dof;
5735 
5736       if ((cp < pStart) || (cp >= pEnd)) continue;
5737       PetscCall(PetscSectionGetDof(section, cp, &dof));
5738       size += dof;
5739     }
5740     if (!values) {
5741       if (csize) *csize = size;
5742       PetscFunctionReturn(0);
5743     }
5744     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5745   } else {
5746     array = *values;
5747   }
5748   size = 0;
5749   PetscCall(VecGetArrayRead(v, &vArray));
5750   if ((point >= pStart) && (point < pEnd)) {
5751     PetscInt           dof, off, d;
5752     const PetscScalar *varr;
5753 
5754     PetscCall(PetscSectionGetDof(section, point, &dof));
5755     PetscCall(PetscSectionGetOffset(section, point, &off));
5756     varr = &vArray[off];
5757     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5758     size += dof;
5759   }
5760   for (p = 0; p < numPoints; ++p) {
5761     const PetscInt     cp = cone[p];
5762     PetscInt           o  = coneO[p];
5763     PetscInt           dof, off, d;
5764     const PetscScalar *varr;
5765 
5766     if ((cp < pStart) || (cp >= pEnd)) continue;
5767     PetscCall(PetscSectionGetDof(section, cp, &dof));
5768     PetscCall(PetscSectionGetOffset(section, cp, &off));
5769     varr = &vArray[off];
5770     if (o >= 0) {
5771       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5772     } else {
5773       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5774     }
5775     size += dof;
5776   }
5777   PetscCall(VecRestoreArrayRead(v, &vArray));
5778   if (!*values) {
5779     if (csize) *csize = size;
5780     *values = array;
5781   } else {
5782     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5783     *csize = size;
5784   }
5785   PetscFunctionReturn(0);
5786 }
5787 
5788 /* Compress out points not in the section */
5789 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5790 {
5791   const PetscInt np = *numPoints;
5792   PetscInt       pStart, pEnd, p, q;
5793 
5794   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5795   for (p = 0, q = 0; p < np; ++p) {
5796     const PetscInt r = points[p * 2];
5797     if ((r >= pStart) && (r < pEnd)) {
5798       points[q * 2]     = r;
5799       points[q * 2 + 1] = points[p * 2 + 1];
5800       ++q;
5801     }
5802   }
5803   *numPoints = q;
5804   return 0;
5805 }
5806 
5807 /* Compressed closure does not apply closure permutation */
5808 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5809 {
5810   const PetscInt *cla = NULL;
5811   PetscInt        np, *pts = NULL;
5812 
5813   PetscFunctionBeginHot;
5814   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5815   if (*clPoints) {
5816     PetscInt dof, off;
5817 
5818     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5819     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5820     PetscCall(ISGetIndices(*clPoints, &cla));
5821     np  = dof / 2;
5822     pts = (PetscInt *)&cla[off];
5823   } else {
5824     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5825     PetscCall(CompressPoints_Private(section, &np, pts));
5826   }
5827   *numPoints = np;
5828   *points    = pts;
5829   *clp       = cla;
5830   PetscFunctionReturn(0);
5831 }
5832 
5833 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5834 {
5835   PetscFunctionBeginHot;
5836   if (!*clPoints) {
5837     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5838   } else {
5839     PetscCall(ISRestoreIndices(*clPoints, clp));
5840   }
5841   *numPoints = 0;
5842   *points    = NULL;
5843   *clSec     = NULL;
5844   *clPoints  = NULL;
5845   *clp       = NULL;
5846   PetscFunctionReturn(0);
5847 }
5848 
5849 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5850 {
5851   PetscInt            offset = 0, p;
5852   const PetscInt    **perms  = NULL;
5853   const PetscScalar **flips  = NULL;
5854 
5855   PetscFunctionBeginHot;
5856   *size = 0;
5857   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5858   for (p = 0; p < numPoints; p++) {
5859     const PetscInt     point = points[2 * p];
5860     const PetscInt    *perm  = perms ? perms[p] : NULL;
5861     const PetscScalar *flip  = flips ? flips[p] : NULL;
5862     PetscInt           dof, off, d;
5863     const PetscScalar *varr;
5864 
5865     PetscCall(PetscSectionGetDof(section, point, &dof));
5866     PetscCall(PetscSectionGetOffset(section, point, &off));
5867     varr = &vArray[off];
5868     if (clperm) {
5869       if (perm) {
5870         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5871       } else {
5872         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5873       }
5874       if (flip) {
5875         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5876       }
5877     } else {
5878       if (perm) {
5879         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5880       } else {
5881         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5882       }
5883       if (flip) {
5884         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5885       }
5886     }
5887     offset += dof;
5888   }
5889   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5890   *size = offset;
5891   PetscFunctionReturn(0);
5892 }
5893 
5894 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[])
5895 {
5896   PetscInt offset = 0, f;
5897 
5898   PetscFunctionBeginHot;
5899   *size = 0;
5900   for (f = 0; f < numFields; ++f) {
5901     PetscInt            p;
5902     const PetscInt    **perms = NULL;
5903     const PetscScalar **flips = NULL;
5904 
5905     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5906     for (p = 0; p < numPoints; p++) {
5907       const PetscInt     point = points[2 * p];
5908       PetscInt           fdof, foff, b;
5909       const PetscScalar *varr;
5910       const PetscInt    *perm = perms ? perms[p] : NULL;
5911       const PetscScalar *flip = flips ? flips[p] : NULL;
5912 
5913       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5914       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5915       varr = &vArray[foff];
5916       if (clperm) {
5917         if (perm) {
5918           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5919         } else {
5920           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5921         }
5922         if (flip) {
5923           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5924         }
5925       } else {
5926         if (perm) {
5927           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5928         } else {
5929           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5930         }
5931         if (flip) {
5932           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5933         }
5934       }
5935       offset += fdof;
5936     }
5937     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5938   }
5939   *size = offset;
5940   PetscFunctionReturn(0);
5941 }
5942 
5943 /*@C
5944   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5945 
5946   Not collective
5947 
5948   Input Parameters:
5949 + dm - The DM
5950 . section - The section describing the layout in v, or NULL to use the default section
5951 . v - The local vector
5952 - point - The point in the DM
5953 
5954   Input/Output Parameters:
5955 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5956 - values - An array to use for the values, or NULL to have it allocated automatically;
5957            if the user provided NULL, it is a borrowed array and should not be freed
5958 
5959 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5960 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5961 $ assembly function, and a user may already have allocated storage for this operation.
5962 $
5963 $ A typical use could be
5964 $
5965 $  values = NULL;
5966 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5967 $  for (cl = 0; cl < clSize; ++cl) {
5968 $    <Compute on closure>
5969 $  }
5970 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5971 $
5972 $ or
5973 $
5974 $  PetscMalloc1(clMaxSize, &values);
5975 $  for (p = pStart; p < pEnd; ++p) {
5976 $    clSize = clMaxSize;
5977 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5978 $    for (cl = 0; cl < clSize; ++cl) {
5979 $      <Compute on closure>
5980 $    }
5981 $  }
5982 $  PetscFree(values);
5983 
5984   Fortran Notes:
5985   Since it returns an array, this routine is only available in Fortran 90, and you must
5986   include petsc.h90 in your code.
5987 
5988   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5989 
5990   Level: intermediate
5991 
5992 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5993 @*/
5994 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5995 {
5996   PetscSection    clSection;
5997   IS              clPoints;
5998   PetscInt       *points = NULL;
5999   const PetscInt *clp, *perm;
6000   PetscInt        depth, numFields, numPoints, asize;
6001 
6002   PetscFunctionBeginHot;
6003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6004   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6005   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6006   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6007   PetscCall(DMPlexGetDepth(dm, &depth));
6008   PetscCall(PetscSectionGetNumFields(section, &numFields));
6009   if (depth == 1 && numFields < 2) {
6010     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6011     PetscFunctionReturn(0);
6012   }
6013   /* Get points */
6014   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6015   /* Get sizes */
6016   asize = 0;
6017   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6018     PetscInt dof;
6019     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6020     asize += dof;
6021   }
6022   if (values) {
6023     const PetscScalar *vArray;
6024     PetscInt           size;
6025 
6026     if (*values) {
6027       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);
6028     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6029     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6030     PetscCall(VecGetArrayRead(v, &vArray));
6031     /* Get values */
6032     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6033     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6034     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6035     /* Cleanup array */
6036     PetscCall(VecRestoreArrayRead(v, &vArray));
6037   }
6038   if (csize) *csize = asize;
6039   /* Cleanup points */
6040   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6041   PetscFunctionReturn(0);
6042 }
6043 
6044 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6045 {
6046   DMLabel            depthLabel;
6047   PetscSection       clSection;
6048   IS                 clPoints;
6049   PetscScalar       *array;
6050   const PetscScalar *vArray;
6051   PetscInt          *points = NULL;
6052   const PetscInt    *clp, *perm = NULL;
6053   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6054 
6055   PetscFunctionBeginHot;
6056   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6057   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6058   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6059   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6060   PetscCall(DMPlexGetDepth(dm, &mdepth));
6061   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6062   PetscCall(PetscSectionGetNumFields(section, &numFields));
6063   if (mdepth == 1 && numFields < 2) {
6064     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6065     PetscFunctionReturn(0);
6066   }
6067   /* Get points */
6068   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6069   for (clsize = 0, p = 0; p < Np; p++) {
6070     PetscInt dof;
6071     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6072     clsize += dof;
6073   }
6074   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6075   /* Filter points */
6076   for (p = 0; p < numPoints * 2; p += 2) {
6077     PetscInt dep;
6078 
6079     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6080     if (dep != depth) continue;
6081     points[Np * 2 + 0] = points[p];
6082     points[Np * 2 + 1] = points[p + 1];
6083     ++Np;
6084   }
6085   /* Get array */
6086   if (!values || !*values) {
6087     PetscInt asize = 0, dof;
6088 
6089     for (p = 0; p < Np * 2; p += 2) {
6090       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6091       asize += dof;
6092     }
6093     if (!values) {
6094       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6095       if (csize) *csize = asize;
6096       PetscFunctionReturn(0);
6097     }
6098     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6099   } else {
6100     array = *values;
6101   }
6102   PetscCall(VecGetArrayRead(v, &vArray));
6103   /* Get values */
6104   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6105   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6106   /* Cleanup points */
6107   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6108   /* Cleanup array */
6109   PetscCall(VecRestoreArrayRead(v, &vArray));
6110   if (!*values) {
6111     if (csize) *csize = size;
6112     *values = array;
6113   } else {
6114     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6115     *csize = size;
6116   }
6117   PetscFunctionReturn(0);
6118 }
6119 
6120 /*@C
6121   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6122 
6123   Not collective
6124 
6125   Input Parameters:
6126 + dm - The DM
6127 . section - The section describing the layout in v, or NULL to use the default section
6128 . v - The local vector
6129 . point - The point in the DM
6130 . csize - The number of values in the closure, or NULL
6131 - values - The array of values, which is a borrowed array and should not be freed
6132 
6133   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
6134 
6135   Fortran Notes:
6136   Since it returns an array, this routine is only available in Fortran 90, and you must
6137   include petsc.h90 in your code.
6138 
6139   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
6140 
6141   Level: intermediate
6142 
6143 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6144 @*/
6145 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6146 {
6147   PetscInt size = 0;
6148 
6149   PetscFunctionBegin;
6150   /* Should work without recalculating size */
6151   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6152   *values = NULL;
6153   PetscFunctionReturn(0);
6154 }
6155 
6156 static inline void add(PetscScalar *x, PetscScalar y)
6157 {
6158   *x += y;
6159 }
6160 static inline void insert(PetscScalar *x, PetscScalar y)
6161 {
6162   *x = y;
6163 }
6164 
6165 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[])
6166 {
6167   PetscInt        cdof;  /* The number of constraints on this point */
6168   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6169   PetscScalar    *a;
6170   PetscInt        off, cind = 0, k;
6171 
6172   PetscFunctionBegin;
6173   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6174   PetscCall(PetscSectionGetOffset(section, point, &off));
6175   a = &array[off];
6176   if (!cdof || setBC) {
6177     if (clperm) {
6178       if (perm) {
6179         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6180       } else {
6181         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6182       }
6183     } else {
6184       if (perm) {
6185         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6186       } else {
6187         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6188       }
6189     }
6190   } else {
6191     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6192     if (clperm) {
6193       if (perm) {
6194         for (k = 0; k < dof; ++k) {
6195           if ((cind < cdof) && (k == cdofs[cind])) {
6196             ++cind;
6197             continue;
6198           }
6199           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6200         }
6201       } else {
6202         for (k = 0; k < dof; ++k) {
6203           if ((cind < cdof) && (k == cdofs[cind])) {
6204             ++cind;
6205             continue;
6206           }
6207           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6208         }
6209       }
6210     } else {
6211       if (perm) {
6212         for (k = 0; k < dof; ++k) {
6213           if ((cind < cdof) && (k == cdofs[cind])) {
6214             ++cind;
6215             continue;
6216           }
6217           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6218         }
6219       } else {
6220         for (k = 0; k < dof; ++k) {
6221           if ((cind < cdof) && (k == cdofs[cind])) {
6222             ++cind;
6223             continue;
6224           }
6225           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6226         }
6227       }
6228     }
6229   }
6230   PetscFunctionReturn(0);
6231 }
6232 
6233 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[])
6234 {
6235   PetscInt        cdof;  /* The number of constraints on this point */
6236   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6237   PetscScalar    *a;
6238   PetscInt        off, cind = 0, k;
6239 
6240   PetscFunctionBegin;
6241   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6242   PetscCall(PetscSectionGetOffset(section, point, &off));
6243   a = &array[off];
6244   if (cdof) {
6245     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6246     if (clperm) {
6247       if (perm) {
6248         for (k = 0; k < dof; ++k) {
6249           if ((cind < cdof) && (k == cdofs[cind])) {
6250             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6251             cind++;
6252           }
6253         }
6254       } else {
6255         for (k = 0; k < dof; ++k) {
6256           if ((cind < cdof) && (k == cdofs[cind])) {
6257             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6258             cind++;
6259           }
6260         }
6261       }
6262     } else {
6263       if (perm) {
6264         for (k = 0; k < dof; ++k) {
6265           if ((cind < cdof) && (k == cdofs[cind])) {
6266             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6267             cind++;
6268           }
6269         }
6270       } else {
6271         for (k = 0; k < dof; ++k) {
6272           if ((cind < cdof) && (k == cdofs[cind])) {
6273             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6274             cind++;
6275           }
6276         }
6277       }
6278     }
6279   }
6280   PetscFunctionReturn(0);
6281 }
6282 
6283 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[])
6284 {
6285   PetscScalar    *a;
6286   PetscInt        fdof, foff, fcdof, foffset = *offset;
6287   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6288   PetscInt        cind = 0, b;
6289 
6290   PetscFunctionBegin;
6291   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6292   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6293   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6294   a = &array[foff];
6295   if (!fcdof || setBC) {
6296     if (clperm) {
6297       if (perm) {
6298         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6299       } else {
6300         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6301       }
6302     } else {
6303       if (perm) {
6304         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6305       } else {
6306         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6307       }
6308     }
6309   } else {
6310     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6311     if (clperm) {
6312       if (perm) {
6313         for (b = 0; b < fdof; b++) {
6314           if ((cind < fcdof) && (b == fcdofs[cind])) {
6315             ++cind;
6316             continue;
6317           }
6318           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6319         }
6320       } else {
6321         for (b = 0; b < fdof; b++) {
6322           if ((cind < fcdof) && (b == fcdofs[cind])) {
6323             ++cind;
6324             continue;
6325           }
6326           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6327         }
6328       }
6329     } else {
6330       if (perm) {
6331         for (b = 0; b < fdof; b++) {
6332           if ((cind < fcdof) && (b == fcdofs[cind])) {
6333             ++cind;
6334             continue;
6335           }
6336           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6337         }
6338       } else {
6339         for (b = 0; b < fdof; b++) {
6340           if ((cind < fcdof) && (b == fcdofs[cind])) {
6341             ++cind;
6342             continue;
6343           }
6344           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6345         }
6346       }
6347     }
6348   }
6349   *offset += fdof;
6350   PetscFunctionReturn(0);
6351 }
6352 
6353 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[])
6354 {
6355   PetscScalar    *a;
6356   PetscInt        fdof, foff, fcdof, foffset = *offset;
6357   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6358   PetscInt        Nc, cind = 0, ncind = 0, b;
6359   PetscBool       ncSet, fcSet;
6360 
6361   PetscFunctionBegin;
6362   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6363   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6364   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6365   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6366   a = &array[foff];
6367   if (fcdof) {
6368     /* We just override fcdof and fcdofs with Ncc and comps */
6369     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6370     if (clperm) {
6371       if (perm) {
6372         if (comps) {
6373           for (b = 0; b < fdof; b++) {
6374             ncSet = fcSet = PETSC_FALSE;
6375             if (b % Nc == comps[ncind]) {
6376               ncind = (ncind + 1) % Ncc;
6377               ncSet = PETSC_TRUE;
6378             }
6379             if ((cind < fcdof) && (b == fcdofs[cind])) {
6380               ++cind;
6381               fcSet = PETSC_TRUE;
6382             }
6383             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6384           }
6385         } else {
6386           for (b = 0; b < fdof; b++) {
6387             if ((cind < fcdof) && (b == fcdofs[cind])) {
6388               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6389               ++cind;
6390             }
6391           }
6392         }
6393       } else {
6394         if (comps) {
6395           for (b = 0; b < fdof; b++) {
6396             ncSet = fcSet = PETSC_FALSE;
6397             if (b % Nc == comps[ncind]) {
6398               ncind = (ncind + 1) % Ncc;
6399               ncSet = PETSC_TRUE;
6400             }
6401             if ((cind < fcdof) && (b == fcdofs[cind])) {
6402               ++cind;
6403               fcSet = PETSC_TRUE;
6404             }
6405             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6406           }
6407         } else {
6408           for (b = 0; b < fdof; b++) {
6409             if ((cind < fcdof) && (b == fcdofs[cind])) {
6410               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6411               ++cind;
6412             }
6413           }
6414         }
6415       }
6416     } else {
6417       if (perm) {
6418         if (comps) {
6419           for (b = 0; b < fdof; b++) {
6420             ncSet = fcSet = PETSC_FALSE;
6421             if (b % Nc == comps[ncind]) {
6422               ncind = (ncind + 1) % Ncc;
6423               ncSet = PETSC_TRUE;
6424             }
6425             if ((cind < fcdof) && (b == fcdofs[cind])) {
6426               ++cind;
6427               fcSet = PETSC_TRUE;
6428             }
6429             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6430           }
6431         } else {
6432           for (b = 0; b < fdof; b++) {
6433             if ((cind < fcdof) && (b == fcdofs[cind])) {
6434               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6435               ++cind;
6436             }
6437           }
6438         }
6439       } else {
6440         if (comps) {
6441           for (b = 0; b < fdof; b++) {
6442             ncSet = fcSet = PETSC_FALSE;
6443             if (b % Nc == comps[ncind]) {
6444               ncind = (ncind + 1) % Ncc;
6445               ncSet = PETSC_TRUE;
6446             }
6447             if ((cind < fcdof) && (b == fcdofs[cind])) {
6448               ++cind;
6449               fcSet = PETSC_TRUE;
6450             }
6451             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6452           }
6453         } else {
6454           for (b = 0; b < fdof; b++) {
6455             if ((cind < fcdof) && (b == fcdofs[cind])) {
6456               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6457               ++cind;
6458             }
6459           }
6460         }
6461       }
6462     }
6463   }
6464   *offset += fdof;
6465   PetscFunctionReturn(0);
6466 }
6467 
6468 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6469 {
6470   PetscScalar    *array;
6471   const PetscInt *cone, *coneO;
6472   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6473 
6474   PetscFunctionBeginHot;
6475   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6476   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6477   PetscCall(DMPlexGetCone(dm, point, &cone));
6478   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6479   PetscCall(VecGetArray(v, &array));
6480   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6481     const PetscInt cp = !p ? point : cone[p - 1];
6482     const PetscInt o  = !p ? 0 : coneO[p - 1];
6483 
6484     if ((cp < pStart) || (cp >= pEnd)) {
6485       dof = 0;
6486       continue;
6487     }
6488     PetscCall(PetscSectionGetDof(section, cp, &dof));
6489     /* ADD_VALUES */
6490     {
6491       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6492       PetscScalar    *a;
6493       PetscInt        cdof, coff, cind = 0, k;
6494 
6495       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6496       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6497       a = &array[coff];
6498       if (!cdof) {
6499         if (o >= 0) {
6500           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6501         } else {
6502           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6503         }
6504       } else {
6505         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6506         if (o >= 0) {
6507           for (k = 0; k < dof; ++k) {
6508             if ((cind < cdof) && (k == cdofs[cind])) {
6509               ++cind;
6510               continue;
6511             }
6512             a[k] += values[off + k];
6513           }
6514         } else {
6515           for (k = 0; k < dof; ++k) {
6516             if ((cind < cdof) && (k == cdofs[cind])) {
6517               ++cind;
6518               continue;
6519             }
6520             a[k] += values[off + dof - k - 1];
6521           }
6522         }
6523       }
6524     }
6525   }
6526   PetscCall(VecRestoreArray(v, &array));
6527   PetscFunctionReturn(0);
6528 }
6529 
6530 /*@C
6531   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6532 
6533   Not collective
6534 
6535   Input Parameters:
6536 + dm - The DM
6537 . section - The section describing the layout in v, or NULL to use the default section
6538 . v - The local vector
6539 . point - The point in the DM
6540 . values - The array of values
6541 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6542          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6543 
6544   Fortran Notes:
6545   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6546 
6547   Level: intermediate
6548 
6549 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6550 @*/
6551 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6552 {
6553   PetscSection    clSection;
6554   IS              clPoints;
6555   PetscScalar    *array;
6556   PetscInt       *points = NULL;
6557   const PetscInt *clp, *clperm = NULL;
6558   PetscInt        depth, numFields, numPoints, p, clsize;
6559 
6560   PetscFunctionBeginHot;
6561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6562   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6563   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6564   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6565   PetscCall(DMPlexGetDepth(dm, &depth));
6566   PetscCall(PetscSectionGetNumFields(section, &numFields));
6567   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6568     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6569     PetscFunctionReturn(0);
6570   }
6571   /* Get points */
6572   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6573   for (clsize = 0, p = 0; p < numPoints; p++) {
6574     PetscInt dof;
6575     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6576     clsize += dof;
6577   }
6578   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6579   /* Get array */
6580   PetscCall(VecGetArray(v, &array));
6581   /* Get values */
6582   if (numFields > 0) {
6583     PetscInt offset = 0, f;
6584     for (f = 0; f < numFields; ++f) {
6585       const PetscInt    **perms = NULL;
6586       const PetscScalar **flips = NULL;
6587 
6588       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6589       switch (mode) {
6590       case INSERT_VALUES:
6591         for (p = 0; p < numPoints; p++) {
6592           const PetscInt     point = points[2 * p];
6593           const PetscInt    *perm  = perms ? perms[p] : NULL;
6594           const PetscScalar *flip  = flips ? flips[p] : NULL;
6595           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6596         }
6597         break;
6598       case INSERT_ALL_VALUES:
6599         for (p = 0; p < numPoints; p++) {
6600           const PetscInt     point = points[2 * p];
6601           const PetscInt    *perm  = perms ? perms[p] : NULL;
6602           const PetscScalar *flip  = flips ? flips[p] : NULL;
6603           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6604         }
6605         break;
6606       case INSERT_BC_VALUES:
6607         for (p = 0; p < numPoints; p++) {
6608           const PetscInt     point = points[2 * p];
6609           const PetscInt    *perm  = perms ? perms[p] : NULL;
6610           const PetscScalar *flip  = flips ? flips[p] : NULL;
6611           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6612         }
6613         break;
6614       case ADD_VALUES:
6615         for (p = 0; p < numPoints; p++) {
6616           const PetscInt     point = points[2 * p];
6617           const PetscInt    *perm  = perms ? perms[p] : NULL;
6618           const PetscScalar *flip  = flips ? flips[p] : NULL;
6619           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6620         }
6621         break;
6622       case ADD_ALL_VALUES:
6623         for (p = 0; p < numPoints; p++) {
6624           const PetscInt     point = points[2 * p];
6625           const PetscInt    *perm  = perms ? perms[p] : NULL;
6626           const PetscScalar *flip  = flips ? flips[p] : NULL;
6627           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6628         }
6629         break;
6630       case ADD_BC_VALUES:
6631         for (p = 0; p < numPoints; p++) {
6632           const PetscInt     point = points[2 * p];
6633           const PetscInt    *perm  = perms ? perms[p] : NULL;
6634           const PetscScalar *flip  = flips ? flips[p] : NULL;
6635           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6636         }
6637         break;
6638       default:
6639         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6640       }
6641       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6642     }
6643   } else {
6644     PetscInt            dof, off;
6645     const PetscInt    **perms = NULL;
6646     const PetscScalar **flips = NULL;
6647 
6648     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6649     switch (mode) {
6650     case INSERT_VALUES:
6651       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6652         const PetscInt     point = points[2 * p];
6653         const PetscInt    *perm  = perms ? perms[p] : NULL;
6654         const PetscScalar *flip  = flips ? flips[p] : NULL;
6655         PetscCall(PetscSectionGetDof(section, point, &dof));
6656         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6657       }
6658       break;
6659     case INSERT_ALL_VALUES:
6660       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6661         const PetscInt     point = points[2 * p];
6662         const PetscInt    *perm  = perms ? perms[p] : NULL;
6663         const PetscScalar *flip  = flips ? flips[p] : NULL;
6664         PetscCall(PetscSectionGetDof(section, point, &dof));
6665         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6666       }
6667       break;
6668     case INSERT_BC_VALUES:
6669       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6670         const PetscInt     point = points[2 * p];
6671         const PetscInt    *perm  = perms ? perms[p] : NULL;
6672         const PetscScalar *flip  = flips ? flips[p] : NULL;
6673         PetscCall(PetscSectionGetDof(section, point, &dof));
6674         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6675       }
6676       break;
6677     case ADD_VALUES:
6678       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6679         const PetscInt     point = points[2 * p];
6680         const PetscInt    *perm  = perms ? perms[p] : NULL;
6681         const PetscScalar *flip  = flips ? flips[p] : NULL;
6682         PetscCall(PetscSectionGetDof(section, point, &dof));
6683         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6684       }
6685       break;
6686     case ADD_ALL_VALUES:
6687       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6688         const PetscInt     point = points[2 * p];
6689         const PetscInt    *perm  = perms ? perms[p] : NULL;
6690         const PetscScalar *flip  = flips ? flips[p] : NULL;
6691         PetscCall(PetscSectionGetDof(section, point, &dof));
6692         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6693       }
6694       break;
6695     case ADD_BC_VALUES:
6696       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6697         const PetscInt     point = points[2 * p];
6698         const PetscInt    *perm  = perms ? perms[p] : NULL;
6699         const PetscScalar *flip  = flips ? flips[p] : NULL;
6700         PetscCall(PetscSectionGetDof(section, point, &dof));
6701         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6702       }
6703       break;
6704     default:
6705       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6706     }
6707     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6708   }
6709   /* Cleanup points */
6710   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6711   /* Cleanup array */
6712   PetscCall(VecRestoreArray(v, &array));
6713   PetscFunctionReturn(0);
6714 }
6715 
6716 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6717 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6718 {
6719   PetscFunctionBegin;
6720   *contains = PETSC_TRUE;
6721   if (label) {
6722     PetscInt fdof;
6723 
6724     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6725     if (!*contains) {
6726       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6727       *offset += fdof;
6728       PetscFunctionReturn(0);
6729     }
6730   }
6731   PetscFunctionReturn(0);
6732 }
6733 
6734 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6735 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)
6736 {
6737   PetscSection    clSection;
6738   IS              clPoints;
6739   PetscScalar    *array;
6740   PetscInt       *points = NULL;
6741   const PetscInt *clp;
6742   PetscInt        numFields, numPoints, p;
6743   PetscInt        offset = 0, f;
6744 
6745   PetscFunctionBeginHot;
6746   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6747   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6748   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6749   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6750   PetscCall(PetscSectionGetNumFields(section, &numFields));
6751   /* Get points */
6752   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6753   /* Get array */
6754   PetscCall(VecGetArray(v, &array));
6755   /* Get values */
6756   for (f = 0; f < numFields; ++f) {
6757     const PetscInt    **perms = NULL;
6758     const PetscScalar **flips = NULL;
6759     PetscBool           contains;
6760 
6761     if (!fieldActive[f]) {
6762       for (p = 0; p < numPoints * 2; p += 2) {
6763         PetscInt fdof;
6764         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6765         offset += fdof;
6766       }
6767       continue;
6768     }
6769     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6770     switch (mode) {
6771     case INSERT_VALUES:
6772       for (p = 0; p < numPoints; p++) {
6773         const PetscInt     point = points[2 * p];
6774         const PetscInt    *perm  = perms ? perms[p] : NULL;
6775         const PetscScalar *flip  = flips ? flips[p] : NULL;
6776         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6777         if (!contains) continue;
6778         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6779       }
6780       break;
6781     case INSERT_ALL_VALUES:
6782       for (p = 0; p < numPoints; p++) {
6783         const PetscInt     point = points[2 * p];
6784         const PetscInt    *perm  = perms ? perms[p] : NULL;
6785         const PetscScalar *flip  = flips ? flips[p] : NULL;
6786         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6787         if (!contains) continue;
6788         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6789       }
6790       break;
6791     case INSERT_BC_VALUES:
6792       for (p = 0; p < numPoints; p++) {
6793         const PetscInt     point = points[2 * p];
6794         const PetscInt    *perm  = perms ? perms[p] : NULL;
6795         const PetscScalar *flip  = flips ? flips[p] : NULL;
6796         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6797         if (!contains) continue;
6798         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6799       }
6800       break;
6801     case ADD_VALUES:
6802       for (p = 0; p < numPoints; p++) {
6803         const PetscInt     point = points[2 * p];
6804         const PetscInt    *perm  = perms ? perms[p] : NULL;
6805         const PetscScalar *flip  = flips ? flips[p] : NULL;
6806         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6807         if (!contains) continue;
6808         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6809       }
6810       break;
6811     case ADD_ALL_VALUES:
6812       for (p = 0; p < numPoints; p++) {
6813         const PetscInt     point = points[2 * p];
6814         const PetscInt    *perm  = perms ? perms[p] : NULL;
6815         const PetscScalar *flip  = flips ? flips[p] : NULL;
6816         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6817         if (!contains) continue;
6818         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6819       }
6820       break;
6821     default:
6822       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6823     }
6824     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6825   }
6826   /* Cleanup points */
6827   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6828   /* Cleanup array */
6829   PetscCall(VecRestoreArray(v, &array));
6830   PetscFunctionReturn(0);
6831 }
6832 
6833 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6834 {
6835   PetscMPIInt rank;
6836   PetscInt    i, j;
6837 
6838   PetscFunctionBegin;
6839   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6840   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6841   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6842   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6843   numCIndices = numCIndices ? numCIndices : numRIndices;
6844   if (!values) PetscFunctionReturn(0);
6845   for (i = 0; i < numRIndices; i++) {
6846     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6847     for (j = 0; j < numCIndices; j++) {
6848 #if defined(PETSC_USE_COMPLEX)
6849       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6850 #else
6851       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6852 #endif
6853     }
6854     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6855   }
6856   PetscFunctionReturn(0);
6857 }
6858 
6859 /*
6860   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6861 
6862   Input Parameters:
6863 + section - The section for this data layout
6864 . islocal - Is the section (and thus indices being requested) local or global?
6865 . point   - The point contributing dofs with these indices
6866 . off     - The global offset of this point
6867 . loff    - The local offset of each field
6868 . setBC   - The flag determining whether to include indices of boundary values
6869 . perm    - A permutation of the dofs on this point, or NULL
6870 - indperm - A permutation of the entire indices array, or NULL
6871 
6872   Output Parameter:
6873 . indices - Indices for dofs on this point
6874 
6875   Level: developer
6876 
6877   Note: The indices could be local or global, depending on the value of 'off'.
6878 */
6879 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6880 {
6881   PetscInt        dof;   /* The number of unknowns on this point */
6882   PetscInt        cdof;  /* The number of constraints on this point */
6883   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6884   PetscInt        cind = 0, k;
6885 
6886   PetscFunctionBegin;
6887   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6888   PetscCall(PetscSectionGetDof(section, point, &dof));
6889   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6890   if (!cdof || setBC) {
6891     for (k = 0; k < dof; ++k) {
6892       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6893       const PetscInt ind    = indperm ? indperm[preind] : preind;
6894 
6895       indices[ind] = off + k;
6896     }
6897   } else {
6898     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6899     for (k = 0; k < dof; ++k) {
6900       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6901       const PetscInt ind    = indperm ? indperm[preind] : preind;
6902 
6903       if ((cind < cdof) && (k == cdofs[cind])) {
6904         /* Insert check for returning constrained indices */
6905         indices[ind] = -(off + k + 1);
6906         ++cind;
6907       } else {
6908         indices[ind] = off + k - (islocal ? 0 : cind);
6909       }
6910     }
6911   }
6912   *loff += dof;
6913   PetscFunctionReturn(0);
6914 }
6915 
6916 /*
6917  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6918 
6919  Input Parameters:
6920 + section - a section (global or local)
6921 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6922 . point - point within section
6923 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6924 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6925 . setBC - identify constrained (boundary condition) points via involution.
6926 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6927 . permsoff - offset
6928 - indperm - index permutation
6929 
6930  Output Parameter:
6931 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6932 . indices - array to hold indices (as defined by section) of each dof associated with point
6933 
6934  Notes:
6935  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6936  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6937  in the local vector.
6938 
6939  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6940  significant).  It is invalid to call with a global section and setBC=true.
6941 
6942  Developer Note:
6943  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6944  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6945  offset could be obtained from the section instead of passing it explicitly as we do now.
6946 
6947  Example:
6948  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6949  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6950  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6951  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.
6952 
6953  Level: developer
6954 */
6955 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[])
6956 {
6957   PetscInt numFields, foff, f;
6958 
6959   PetscFunctionBegin;
6960   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6961   PetscCall(PetscSectionGetNumFields(section, &numFields));
6962   for (f = 0, foff = 0; f < numFields; ++f) {
6963     PetscInt        fdof, cfdof;
6964     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6965     PetscInt        cind = 0, b;
6966     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6967 
6968     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6969     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6970     if (!cfdof || setBC) {
6971       for (b = 0; b < fdof; ++b) {
6972         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6973         const PetscInt ind    = indperm ? indperm[preind] : preind;
6974 
6975         indices[ind] = off + foff + b;
6976       }
6977     } else {
6978       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6979       for (b = 0; b < fdof; ++b) {
6980         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6981         const PetscInt ind    = indperm ? indperm[preind] : preind;
6982 
6983         if ((cind < cfdof) && (b == fcdofs[cind])) {
6984           indices[ind] = -(off + foff + b + 1);
6985           ++cind;
6986         } else {
6987           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6988         }
6989       }
6990     }
6991     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6992     foffs[f] += fdof;
6993   }
6994   PetscFunctionReturn(0);
6995 }
6996 
6997 /*
6998   This version believes the globalSection offsets for each field, rather than just the point offset
6999 
7000  . foffs - The offset into 'indices' for each field, since it is segregated by field
7001 
7002  Notes:
7003  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7004  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7005 */
7006 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7007 {
7008   PetscInt numFields, foff, f;
7009 
7010   PetscFunctionBegin;
7011   PetscCall(PetscSectionGetNumFields(section, &numFields));
7012   for (f = 0; f < numFields; ++f) {
7013     PetscInt        fdof, cfdof;
7014     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7015     PetscInt        cind = 0, b;
7016     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7017 
7018     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7019     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7020     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7021     if (!cfdof) {
7022       for (b = 0; b < fdof; ++b) {
7023         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7024         const PetscInt ind    = indperm ? indperm[preind] : preind;
7025 
7026         indices[ind] = foff + b;
7027       }
7028     } else {
7029       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7030       for (b = 0; b < fdof; ++b) {
7031         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7032         const PetscInt ind    = indperm ? indperm[preind] : preind;
7033 
7034         if ((cind < cfdof) && (b == fcdofs[cind])) {
7035           indices[ind] = -(foff + b + 1);
7036           ++cind;
7037         } else {
7038           indices[ind] = foff + b - cind;
7039         }
7040       }
7041     }
7042     foffs[f] += fdof;
7043   }
7044   PetscFunctionReturn(0);
7045 }
7046 
7047 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)
7048 {
7049   Mat             cMat;
7050   PetscSection    aSec, cSec;
7051   IS              aIS;
7052   PetscInt        aStart = -1, aEnd = -1;
7053   const PetscInt *anchors;
7054   PetscInt        numFields, f, p, q, newP = 0;
7055   PetscInt        newNumPoints = 0, newNumIndices = 0;
7056   PetscInt       *newPoints, *indices, *newIndices;
7057   PetscInt        maxAnchor, maxDof;
7058   PetscInt        newOffsets[32];
7059   PetscInt       *pointMatOffsets[32];
7060   PetscInt       *newPointOffsets[32];
7061   PetscScalar    *pointMat[32];
7062   PetscScalar    *newValues      = NULL, *tmpValues;
7063   PetscBool       anyConstrained = PETSC_FALSE;
7064 
7065   PetscFunctionBegin;
7066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7067   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7068   PetscCall(PetscSectionGetNumFields(section, &numFields));
7069 
7070   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7071   /* if there are point-to-point constraints */
7072   if (aSec) {
7073     PetscCall(PetscArrayzero(newOffsets, 32));
7074     PetscCall(ISGetIndices(aIS, &anchors));
7075     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7076     /* figure out how many points are going to be in the new element matrix
7077      * (we allow double counting, because it's all just going to be summed
7078      * into the global matrix anyway) */
7079     for (p = 0; p < 2 * numPoints; p += 2) {
7080       PetscInt b    = points[p];
7081       PetscInt bDof = 0, bSecDof;
7082 
7083       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7084       if (!bSecDof) continue;
7085       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7086       if (bDof) {
7087         /* this point is constrained */
7088         /* it is going to be replaced by its anchors */
7089         PetscInt bOff, q;
7090 
7091         anyConstrained = PETSC_TRUE;
7092         newNumPoints += bDof;
7093         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7094         for (q = 0; q < bDof; q++) {
7095           PetscInt a = anchors[bOff + q];
7096           PetscInt aDof;
7097 
7098           PetscCall(PetscSectionGetDof(section, a, &aDof));
7099           newNumIndices += aDof;
7100           for (f = 0; f < numFields; ++f) {
7101             PetscInt fDof;
7102 
7103             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7104             newOffsets[f + 1] += fDof;
7105           }
7106         }
7107       } else {
7108         /* this point is not constrained */
7109         newNumPoints++;
7110         newNumIndices += bSecDof;
7111         for (f = 0; f < numFields; ++f) {
7112           PetscInt fDof;
7113 
7114           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7115           newOffsets[f + 1] += fDof;
7116         }
7117       }
7118     }
7119   }
7120   if (!anyConstrained) {
7121     if (outNumPoints) *outNumPoints = 0;
7122     if (outNumIndices) *outNumIndices = 0;
7123     if (outPoints) *outPoints = NULL;
7124     if (outValues) *outValues = NULL;
7125     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7126     PetscFunctionReturn(0);
7127   }
7128 
7129   if (outNumPoints) *outNumPoints = newNumPoints;
7130   if (outNumIndices) *outNumIndices = newNumIndices;
7131 
7132   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7133 
7134   if (!outPoints && !outValues) {
7135     if (offsets) {
7136       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7137     }
7138     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7139     PetscFunctionReturn(0);
7140   }
7141 
7142   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7143 
7144   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7145 
7146   /* workspaces */
7147   if (numFields) {
7148     for (f = 0; f < numFields; f++) {
7149       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7150       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7151     }
7152   } else {
7153     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7154     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7155   }
7156 
7157   /* get workspaces for the point-to-point matrices */
7158   if (numFields) {
7159     PetscInt totalOffset, totalMatOffset;
7160 
7161     for (p = 0; p < numPoints; p++) {
7162       PetscInt b    = points[2 * p];
7163       PetscInt bDof = 0, bSecDof;
7164 
7165       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7166       if (!bSecDof) {
7167         for (f = 0; f < numFields; f++) {
7168           newPointOffsets[f][p + 1] = 0;
7169           pointMatOffsets[f][p + 1] = 0;
7170         }
7171         continue;
7172       }
7173       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7174       if (bDof) {
7175         for (f = 0; f < numFields; f++) {
7176           PetscInt fDof, q, bOff, allFDof = 0;
7177 
7178           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7179           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7180           for (q = 0; q < bDof; q++) {
7181             PetscInt a = anchors[bOff + q];
7182             PetscInt aFDof;
7183 
7184             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7185             allFDof += aFDof;
7186           }
7187           newPointOffsets[f][p + 1] = allFDof;
7188           pointMatOffsets[f][p + 1] = fDof * allFDof;
7189         }
7190       } else {
7191         for (f = 0; f < numFields; f++) {
7192           PetscInt fDof;
7193 
7194           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7195           newPointOffsets[f][p + 1] = fDof;
7196           pointMatOffsets[f][p + 1] = 0;
7197         }
7198       }
7199     }
7200     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7201       newPointOffsets[f][0] = totalOffset;
7202       pointMatOffsets[f][0] = totalMatOffset;
7203       for (p = 0; p < numPoints; p++) {
7204         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7205         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7206       }
7207       totalOffset    = newPointOffsets[f][numPoints];
7208       totalMatOffset = pointMatOffsets[f][numPoints];
7209       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7210     }
7211   } else {
7212     for (p = 0; p < numPoints; p++) {
7213       PetscInt b    = points[2 * p];
7214       PetscInt bDof = 0, bSecDof;
7215 
7216       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7217       if (!bSecDof) {
7218         newPointOffsets[0][p + 1] = 0;
7219         pointMatOffsets[0][p + 1] = 0;
7220         continue;
7221       }
7222       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7223       if (bDof) {
7224         PetscInt bOff, q, allDof = 0;
7225 
7226         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7227         for (q = 0; q < bDof; q++) {
7228           PetscInt a = anchors[bOff + q], aDof;
7229 
7230           PetscCall(PetscSectionGetDof(section, a, &aDof));
7231           allDof += aDof;
7232         }
7233         newPointOffsets[0][p + 1] = allDof;
7234         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7235       } else {
7236         newPointOffsets[0][p + 1] = bSecDof;
7237         pointMatOffsets[0][p + 1] = 0;
7238       }
7239     }
7240     newPointOffsets[0][0] = 0;
7241     pointMatOffsets[0][0] = 0;
7242     for (p = 0; p < numPoints; p++) {
7243       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7244       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7245     }
7246     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7247   }
7248 
7249   /* output arrays */
7250   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7251 
7252   /* get the point-to-point matrices; construct newPoints */
7253   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7254   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7255   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7256   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7257   if (numFields) {
7258     for (p = 0, newP = 0; p < numPoints; p++) {
7259       PetscInt b    = points[2 * p];
7260       PetscInt o    = points[2 * p + 1];
7261       PetscInt bDof = 0, bSecDof;
7262 
7263       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7264       if (!bSecDof) continue;
7265       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7266       if (bDof) {
7267         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7268 
7269         fStart[0] = 0;
7270         fEnd[0]   = 0;
7271         for (f = 0; f < numFields; f++) {
7272           PetscInt fDof;
7273 
7274           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7275           fStart[f + 1] = fStart[f] + fDof;
7276           fEnd[f + 1]   = fStart[f + 1];
7277         }
7278         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7279         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7280 
7281         fAnchorStart[0] = 0;
7282         fAnchorEnd[0]   = 0;
7283         for (f = 0; f < numFields; f++) {
7284           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7285 
7286           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7287           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7288         }
7289         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7290         for (q = 0; q < bDof; q++) {
7291           PetscInt a = anchors[bOff + q], aOff;
7292 
7293           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7294           newPoints[2 * (newP + q)]     = a;
7295           newPoints[2 * (newP + q) + 1] = 0;
7296           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7297           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7298         }
7299         newP += bDof;
7300 
7301         if (outValues) {
7302           /* get the point-to-point submatrix */
7303           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]));
7304         }
7305       } else {
7306         newPoints[2 * newP]     = b;
7307         newPoints[2 * newP + 1] = o;
7308         newP++;
7309       }
7310     }
7311   } else {
7312     for (p = 0; p < numPoints; p++) {
7313       PetscInt b    = points[2 * p];
7314       PetscInt o    = points[2 * p + 1];
7315       PetscInt bDof = 0, bSecDof;
7316 
7317       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7318       if (!bSecDof) continue;
7319       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7320       if (bDof) {
7321         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7322 
7323         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7324         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7325 
7326         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7327         for (q = 0; q < bDof; q++) {
7328           PetscInt a = anchors[bOff + q], aOff;
7329 
7330           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7331 
7332           newPoints[2 * (newP + q)]     = a;
7333           newPoints[2 * (newP + q) + 1] = 0;
7334           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7335           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7336         }
7337         newP += bDof;
7338 
7339         /* get the point-to-point submatrix */
7340         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7341       } else {
7342         newPoints[2 * newP]     = b;
7343         newPoints[2 * newP + 1] = o;
7344         newP++;
7345       }
7346     }
7347   }
7348 
7349   if (outValues) {
7350     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7351     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7352     /* multiply constraints on the right */
7353     if (numFields) {
7354       for (f = 0; f < numFields; f++) {
7355         PetscInt oldOff = offsets[f];
7356 
7357         for (p = 0; p < numPoints; p++) {
7358           PetscInt cStart = newPointOffsets[f][p];
7359           PetscInt b      = points[2 * p];
7360           PetscInt c, r, k;
7361           PetscInt dof;
7362 
7363           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7364           if (!dof) continue;
7365           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7366             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7367             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7368 
7369             for (r = 0; r < numIndices; r++) {
7370               for (c = 0; c < nCols; c++) {
7371                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7372               }
7373             }
7374           } else {
7375             /* copy this column as is */
7376             for (r = 0; r < numIndices; r++) {
7377               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7378             }
7379           }
7380           oldOff += dof;
7381         }
7382       }
7383     } else {
7384       PetscInt oldOff = 0;
7385       for (p = 0; p < numPoints; p++) {
7386         PetscInt cStart = newPointOffsets[0][p];
7387         PetscInt b      = points[2 * p];
7388         PetscInt c, r, k;
7389         PetscInt dof;
7390 
7391         PetscCall(PetscSectionGetDof(section, b, &dof));
7392         if (!dof) continue;
7393         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7394           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7395           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7396 
7397           for (r = 0; r < numIndices; r++) {
7398             for (c = 0; c < nCols; c++) {
7399               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7400             }
7401           }
7402         } else {
7403           /* copy this column as is */
7404           for (r = 0; r < numIndices; r++) {
7405             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7406           }
7407         }
7408         oldOff += dof;
7409       }
7410     }
7411 
7412     if (multiplyLeft) {
7413       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7414       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7415       /* multiply constraints transpose on the left */
7416       if (numFields) {
7417         for (f = 0; f < numFields; f++) {
7418           PetscInt oldOff = offsets[f];
7419 
7420           for (p = 0; p < numPoints; p++) {
7421             PetscInt rStart = newPointOffsets[f][p];
7422             PetscInt b      = points[2 * p];
7423             PetscInt c, r, k;
7424             PetscInt dof;
7425 
7426             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7427             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7428               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7429               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7430 
7431               for (r = 0; r < nRows; r++) {
7432                 for (c = 0; c < newNumIndices; c++) {
7433                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7434                 }
7435               }
7436             } else {
7437               /* copy this row as is */
7438               for (r = 0; r < dof; r++) {
7439                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7440               }
7441             }
7442             oldOff += dof;
7443           }
7444         }
7445       } else {
7446         PetscInt oldOff = 0;
7447 
7448         for (p = 0; p < numPoints; p++) {
7449           PetscInt rStart = newPointOffsets[0][p];
7450           PetscInt b      = points[2 * p];
7451           PetscInt c, r, k;
7452           PetscInt dof;
7453 
7454           PetscCall(PetscSectionGetDof(section, b, &dof));
7455           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7456             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7457             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7458 
7459             for (r = 0; r < nRows; r++) {
7460               for (c = 0; c < newNumIndices; c++) {
7461                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7462               }
7463             }
7464           } else {
7465             /* copy this row as is */
7466             for (r = 0; r < dof; r++) {
7467               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7468             }
7469           }
7470           oldOff += dof;
7471         }
7472       }
7473 
7474       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7475     } else {
7476       newValues = tmpValues;
7477     }
7478   }
7479 
7480   /* clean up */
7481   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7482   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7483 
7484   if (numFields) {
7485     for (f = 0; f < numFields; f++) {
7486       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7487       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7488       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7489     }
7490   } else {
7491     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7492     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7493     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7494   }
7495   PetscCall(ISRestoreIndices(aIS, &anchors));
7496 
7497   /* output */
7498   if (outPoints) {
7499     *outPoints = newPoints;
7500   } else {
7501     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7502   }
7503   if (outValues) *outValues = newValues;
7504   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7505   PetscFunctionReturn(0);
7506 }
7507 
7508 /*@C
7509   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7510 
7511   Not collective
7512 
7513   Input Parameters:
7514 + dm         - The DM
7515 . section    - The PetscSection describing the points (a local section)
7516 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7517 . point      - The point defining the closure
7518 - useClPerm  - Use the closure point permutation if available
7519 
7520   Output Parameters:
7521 + numIndices - The number of dof indices in the closure of point with the input sections
7522 . indices    - The dof indices
7523 . outOffsets - Array to write the field offsets into, or NULL
7524 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7525 
7526   Notes:
7527   Must call DMPlexRestoreClosureIndices() to free allocated memory
7528 
7529   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7530   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7531   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7532   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7533   indices (with the above semantics) are implied.
7534 
7535   Level: advanced
7536 
7537 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7538 @*/
7539 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7540 {
7541   /* Closure ordering */
7542   PetscSection    clSection;
7543   IS              clPoints;
7544   const PetscInt *clp;
7545   PetscInt       *points;
7546   const PetscInt *clperm = NULL;
7547   /* Dof permutation and sign flips */
7548   const PetscInt    **perms[32] = {NULL};
7549   const PetscScalar **flips[32] = {NULL};
7550   PetscScalar        *valCopy   = NULL;
7551   /* Hanging node constraints */
7552   PetscInt    *pointsC = NULL;
7553   PetscScalar *valuesC = NULL;
7554   PetscInt     NclC, NiC;
7555 
7556   PetscInt *idx;
7557   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7558   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7559 
7560   PetscFunctionBeginHot;
7561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7562   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7563   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7564   if (numIndices) PetscValidIntPointer(numIndices, 6);
7565   if (indices) PetscValidPointer(indices, 7);
7566   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7567   if (values) PetscValidPointer(values, 9);
7568   PetscCall(PetscSectionGetNumFields(section, &Nf));
7569   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7570   PetscCall(PetscArrayzero(offsets, 32));
7571   /* 1) Get points in closure */
7572   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7573   if (useClPerm) {
7574     PetscInt depth, clsize;
7575     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7576     for (clsize = 0, p = 0; p < Ncl; p++) {
7577       PetscInt dof;
7578       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7579       clsize += dof;
7580     }
7581     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7582   }
7583   /* 2) Get number of indices on these points and field offsets from section */
7584   for (p = 0; p < Ncl * 2; p += 2) {
7585     PetscInt dof, fdof;
7586 
7587     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7588     for (f = 0; f < Nf; ++f) {
7589       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7590       offsets[f + 1] += fdof;
7591     }
7592     Ni += dof;
7593   }
7594   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7595   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7596   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7597   for (f = 0; f < PetscMax(1, Nf); ++f) {
7598     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7599     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7600     /* may need to apply sign changes to the element matrix */
7601     if (values && flips[f]) {
7602       PetscInt foffset = offsets[f];
7603 
7604       for (p = 0; p < Ncl; ++p) {
7605         PetscInt           pnt  = points[2 * p], fdof;
7606         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7607 
7608         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7609         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7610         if (flip) {
7611           PetscInt i, j, k;
7612 
7613           if (!valCopy) {
7614             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7615             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7616             *values = valCopy;
7617           }
7618           for (i = 0; i < fdof; ++i) {
7619             PetscScalar fval = flip[i];
7620 
7621             for (k = 0; k < Ni; ++k) {
7622               valCopy[Ni * (foffset + i) + k] *= fval;
7623               valCopy[Ni * k + (foffset + i)] *= fval;
7624             }
7625           }
7626         }
7627         foffset += fdof;
7628       }
7629     }
7630   }
7631   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7632   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7633   if (NclC) {
7634     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7635     for (f = 0; f < PetscMax(1, Nf); ++f) {
7636       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7637       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7638     }
7639     for (f = 0; f < PetscMax(1, Nf); ++f) {
7640       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7641       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7642     }
7643     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7644     Ncl    = NclC;
7645     Ni     = NiC;
7646     points = pointsC;
7647     if (values) *values = valuesC;
7648   }
7649   /* 5) Calculate indices */
7650   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7651   if (Nf) {
7652     PetscInt  idxOff;
7653     PetscBool useFieldOffsets;
7654 
7655     if (outOffsets) {
7656       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7657     }
7658     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7659     if (useFieldOffsets) {
7660       for (p = 0; p < Ncl; ++p) {
7661         const PetscInt pnt = points[p * 2];
7662 
7663         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7664       }
7665     } else {
7666       for (p = 0; p < Ncl; ++p) {
7667         const PetscInt pnt = points[p * 2];
7668 
7669         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7670         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7671          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7672          * global section. */
7673         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7674       }
7675     }
7676   } else {
7677     PetscInt off = 0, idxOff;
7678 
7679     for (p = 0; p < Ncl; ++p) {
7680       const PetscInt  pnt  = points[p * 2];
7681       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7682 
7683       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7684       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7685        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7686       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7687     }
7688   }
7689   /* 6) Cleanup */
7690   for (f = 0; f < PetscMax(1, Nf); ++f) {
7691     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7692     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7693   }
7694   if (NclC) {
7695     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7696   } else {
7697     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7698   }
7699 
7700   if (numIndices) *numIndices = Ni;
7701   if (indices) *indices = idx;
7702   PetscFunctionReturn(0);
7703 }
7704 
7705 /*@C
7706   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7707 
7708   Not collective
7709 
7710   Input Parameters:
7711 + dm         - The DM
7712 . section    - The PetscSection describing the points (a local section)
7713 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7714 . point      - The point defining the closure
7715 - useClPerm  - Use the closure point permutation if available
7716 
7717   Output Parameters:
7718 + numIndices - The number of dof indices in the closure of point with the input sections
7719 . indices    - The dof indices
7720 . outOffsets - Array to write the field offsets into, or NULL
7721 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7722 
7723   Notes:
7724   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7725 
7726   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7727   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7728   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7729   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7730   indices (with the above semantics) are implied.
7731 
7732   Level: advanced
7733 
7734 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7735 @*/
7736 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7737 {
7738   PetscFunctionBegin;
7739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7740   PetscValidPointer(indices, 7);
7741   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7742   PetscFunctionReturn(0);
7743 }
7744 
7745 /*@C
7746   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7747 
7748   Not collective
7749 
7750   Input Parameters:
7751 + dm - The DM
7752 . section - The section describing the layout in v, or NULL to use the default section
7753 . globalSection - The section describing the layout in v, or NULL to use the default global section
7754 . A - The matrix
7755 . point - The point in the DM
7756 . values - The array of values
7757 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7758 
7759   Fortran Notes:
7760   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7761 
7762   Level: intermediate
7763 
7764 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7765 @*/
7766 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7767 {
7768   DM_Plex           *mesh = (DM_Plex *)dm->data;
7769   PetscInt          *indices;
7770   PetscInt           numIndices;
7771   const PetscScalar *valuesOrig = values;
7772   PetscErrorCode     ierr;
7773 
7774   PetscFunctionBegin;
7775   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7776   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7777   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7778   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7779   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7780   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7781 
7782   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7783 
7784   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7785   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7786   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7787   if (ierr) {
7788     PetscMPIInt rank;
7789 
7790     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7791     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7792     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7793     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7794     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7795     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7796   }
7797   if (mesh->printFEM > 1) {
7798     PetscInt i;
7799     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7800     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7801     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7802   }
7803 
7804   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7805   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7806   PetscFunctionReturn(0);
7807 }
7808 
7809 /*@C
7810   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7811 
7812   Not collective
7813 
7814   Input Parameters:
7815 + dmRow - The DM for the row fields
7816 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7817 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7818 . dmCol - The DM for the column fields
7819 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7820 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7821 . A - The matrix
7822 . point - The point in the DMs
7823 . values - The array of values
7824 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7825 
7826   Level: intermediate
7827 
7828 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7829 @*/
7830 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7831 {
7832   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7833   PetscInt          *indicesRow, *indicesCol;
7834   PetscInt           numIndicesRow, numIndicesCol;
7835   const PetscScalar *valuesOrig = values;
7836   PetscErrorCode     ierr;
7837 
7838   PetscFunctionBegin;
7839   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7840   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7841   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7842   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7843   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7844   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7845   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7846   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7847   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7848   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7849   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7850 
7851   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7852   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7853 
7854   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7855   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7856   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7857   if (ierr) {
7858     PetscMPIInt rank;
7859 
7860     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7861     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7862     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7863     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7864     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7865     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7866   }
7867 
7868   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7869   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7870   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7871   PetscFunctionReturn(0);
7872 }
7873 
7874 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7875 {
7876   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7877   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7878   PetscInt       *cpoints = NULL;
7879   PetscInt       *findices, *cindices;
7880   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7881   PetscInt        foffsets[32], coffsets[32];
7882   DMPolytopeType  ct;
7883   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7884   PetscErrorCode  ierr;
7885 
7886   PetscFunctionBegin;
7887   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7888   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7889   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7890   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7891   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7892   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7893   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7894   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7895   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7896   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7897   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7898   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7899   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7900   PetscCall(PetscArrayzero(foffsets, 32));
7901   PetscCall(PetscArrayzero(coffsets, 32));
7902   /* Column indices */
7903   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7904   maxFPoints = numCPoints;
7905   /* Compress out points not in the section */
7906   /*   TODO: Squeeze out points with 0 dof as well */
7907   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7908   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7909     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7910       cpoints[q * 2]     = cpoints[p];
7911       cpoints[q * 2 + 1] = cpoints[p + 1];
7912       ++q;
7913     }
7914   }
7915   numCPoints = q;
7916   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7917     PetscInt fdof;
7918 
7919     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7920     if (!dof) continue;
7921     for (f = 0; f < numFields; ++f) {
7922       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7923       coffsets[f + 1] += fdof;
7924     }
7925     numCIndices += dof;
7926   }
7927   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7928   /* Row indices */
7929   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7930   {
7931     DMPlexTransform tr;
7932     DMPolytopeType *rct;
7933     PetscInt       *rsize, *rcone, *rornt, Nt;
7934 
7935     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7936     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7937     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7938     numSubcells = rsize[Nt - 1];
7939     PetscCall(DMPlexTransformDestroy(&tr));
7940   }
7941   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7942   for (r = 0, q = 0; r < numSubcells; ++r) {
7943     /* TODO Map from coarse to fine cells */
7944     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7945     /* Compress out points not in the section */
7946     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7947     for (p = 0; p < numFPoints * 2; p += 2) {
7948       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7949         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7950         if (!dof) continue;
7951         for (s = 0; s < q; ++s)
7952           if (fpoints[p] == ftotpoints[s * 2]) break;
7953         if (s < q) continue;
7954         ftotpoints[q * 2]     = fpoints[p];
7955         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7956         ++q;
7957       }
7958     }
7959     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7960   }
7961   numFPoints = q;
7962   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7963     PetscInt fdof;
7964 
7965     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7966     if (!dof) continue;
7967     for (f = 0; f < numFields; ++f) {
7968       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7969       foffsets[f + 1] += fdof;
7970     }
7971     numFIndices += dof;
7972   }
7973   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7974 
7975   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7976   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7977   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7978   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7979   if (numFields) {
7980     const PetscInt **permsF[32] = {NULL};
7981     const PetscInt **permsC[32] = {NULL};
7982 
7983     for (f = 0; f < numFields; f++) {
7984       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7985       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7986     }
7987     for (p = 0; p < numFPoints; p++) {
7988       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7989       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7990     }
7991     for (p = 0; p < numCPoints; p++) {
7992       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7993       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7994     }
7995     for (f = 0; f < numFields; f++) {
7996       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7997       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7998     }
7999   } else {
8000     const PetscInt **permsF = NULL;
8001     const PetscInt **permsC = NULL;
8002 
8003     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8004     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8005     for (p = 0, off = 0; p < numFPoints; p++) {
8006       const PetscInt *perm = permsF ? permsF[p] : NULL;
8007 
8008       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8009       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8010     }
8011     for (p = 0, off = 0; p < numCPoints; p++) {
8012       const PetscInt *perm = permsC ? permsC[p] : NULL;
8013 
8014       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8015       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8016     }
8017     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8018     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8019   }
8020   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8021   /* TODO: flips */
8022   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8023   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8024   if (ierr) {
8025     PetscMPIInt rank;
8026 
8027     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8028     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8029     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8030     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8031     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8032   }
8033   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8034   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8035   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8036   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8037   PetscFunctionReturn(0);
8038 }
8039 
8040 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8041 {
8042   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8043   PetscInt       *cpoints = NULL;
8044   PetscInt        foffsets[32], coffsets[32];
8045   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8046   DMPolytopeType  ct;
8047   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8048 
8049   PetscFunctionBegin;
8050   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8051   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8052   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8053   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8054   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8055   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8056   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8057   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8058   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8059   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8060   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8061   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8062   PetscCall(PetscArrayzero(foffsets, 32));
8063   PetscCall(PetscArrayzero(coffsets, 32));
8064   /* Column indices */
8065   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8066   maxFPoints = numCPoints;
8067   /* Compress out points not in the section */
8068   /*   TODO: Squeeze out points with 0 dof as well */
8069   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8070   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8071     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8072       cpoints[q * 2]     = cpoints[p];
8073       cpoints[q * 2 + 1] = cpoints[p + 1];
8074       ++q;
8075     }
8076   }
8077   numCPoints = q;
8078   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8079     PetscInt fdof;
8080 
8081     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8082     if (!dof) continue;
8083     for (f = 0; f < numFields; ++f) {
8084       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8085       coffsets[f + 1] += fdof;
8086     }
8087     numCIndices += dof;
8088   }
8089   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8090   /* Row indices */
8091   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8092   {
8093     DMPlexTransform tr;
8094     DMPolytopeType *rct;
8095     PetscInt       *rsize, *rcone, *rornt, Nt;
8096 
8097     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8098     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8099     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8100     numSubcells = rsize[Nt - 1];
8101     PetscCall(DMPlexTransformDestroy(&tr));
8102   }
8103   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8104   for (r = 0, q = 0; r < numSubcells; ++r) {
8105     /* TODO Map from coarse to fine cells */
8106     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8107     /* Compress out points not in the section */
8108     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8109     for (p = 0; p < numFPoints * 2; p += 2) {
8110       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8111         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8112         if (!dof) continue;
8113         for (s = 0; s < q; ++s)
8114           if (fpoints[p] == ftotpoints[s * 2]) break;
8115         if (s < q) continue;
8116         ftotpoints[q * 2]     = fpoints[p];
8117         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8118         ++q;
8119       }
8120     }
8121     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8122   }
8123   numFPoints = q;
8124   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8125     PetscInt fdof;
8126 
8127     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8128     if (!dof) continue;
8129     for (f = 0; f < numFields; ++f) {
8130       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8131       foffsets[f + 1] += fdof;
8132     }
8133     numFIndices += dof;
8134   }
8135   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8136 
8137   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8138   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8139   if (numFields) {
8140     const PetscInt **permsF[32] = {NULL};
8141     const PetscInt **permsC[32] = {NULL};
8142 
8143     for (f = 0; f < numFields; f++) {
8144       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8145       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8146     }
8147     for (p = 0; p < numFPoints; p++) {
8148       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8149       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8150     }
8151     for (p = 0; p < numCPoints; p++) {
8152       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8153       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8154     }
8155     for (f = 0; f < numFields; f++) {
8156       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8157       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8158     }
8159   } else {
8160     const PetscInt **permsF = NULL;
8161     const PetscInt **permsC = NULL;
8162 
8163     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8164     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8165     for (p = 0, off = 0; p < numFPoints; p++) {
8166       const PetscInt *perm = permsF ? permsF[p] : NULL;
8167 
8168       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8169       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8170     }
8171     for (p = 0, off = 0; p < numCPoints; p++) {
8172       const PetscInt *perm = permsC ? permsC[p] : NULL;
8173 
8174       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8175       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8176     }
8177     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8178     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8179   }
8180   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8181   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8182   PetscFunctionReturn(0);
8183 }
8184 
8185 /*@C
8186   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8187 
8188   Input Parameter:
8189 . dm   - The DMPlex object
8190 
8191   Output Parameter:
8192 . cellHeight - The height of a cell
8193 
8194   Level: developer
8195 
8196 .seealso `DMPlexSetVTKCellHeight()`
8197 @*/
8198 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8199 {
8200   DM_Plex *mesh = (DM_Plex *)dm->data;
8201 
8202   PetscFunctionBegin;
8203   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8204   PetscValidIntPointer(cellHeight, 2);
8205   *cellHeight = mesh->vtkCellHeight;
8206   PetscFunctionReturn(0);
8207 }
8208 
8209 /*@C
8210   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8211 
8212   Input Parameters:
8213 + dm   - The DMPlex object
8214 - cellHeight - The height of a cell
8215 
8216   Level: developer
8217 
8218 .seealso `DMPlexGetVTKCellHeight()`
8219 @*/
8220 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8221 {
8222   DM_Plex *mesh = (DM_Plex *)dm->data;
8223 
8224   PetscFunctionBegin;
8225   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8226   mesh->vtkCellHeight = cellHeight;
8227   PetscFunctionReturn(0);
8228 }
8229 
8230 /*@
8231   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8232 
8233   Input Parameter:
8234 . dm - The DMPlex object
8235 
8236   Output Parameters:
8237 + gcStart - The first ghost cell, or NULL
8238 - gcEnd   - The upper bound on ghost cells, or NULL
8239 
8240   Level: advanced
8241 
8242 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8243 @*/
8244 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8245 {
8246   DMLabel ctLabel;
8247 
8248   PetscFunctionBegin;
8249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8250   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8251   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8252   // Reset label for fast lookup
8253   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8254   PetscFunctionReturn(0);
8255 }
8256 
8257 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8258 {
8259   PetscSection section, globalSection;
8260   PetscInt    *numbers, p;
8261 
8262   PetscFunctionBegin;
8263   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8264   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8265   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8266   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8267   PetscCall(PetscSectionSetUp(section));
8268   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8269   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8270   for (p = pStart; p < pEnd; ++p) {
8271     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8272     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8273     else numbers[p - pStart] += shift;
8274   }
8275   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8276   if (globalSize) {
8277     PetscLayout layout;
8278     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8279     PetscCall(PetscLayoutGetSize(layout, globalSize));
8280     PetscCall(PetscLayoutDestroy(&layout));
8281   }
8282   PetscCall(PetscSectionDestroy(&section));
8283   PetscCall(PetscSectionDestroy(&globalSection));
8284   PetscFunctionReturn(0);
8285 }
8286 
8287 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8288 {
8289   PetscInt cellHeight, cStart, cEnd;
8290 
8291   PetscFunctionBegin;
8292   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8293   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8294   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8295   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8296   PetscFunctionReturn(0);
8297 }
8298 
8299 /*@
8300   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8301 
8302   Input Parameter:
8303 . dm   - The DMPlex object
8304 
8305   Output Parameter:
8306 . globalCellNumbers - Global cell numbers for all cells on this process
8307 
8308   Level: developer
8309 
8310 .seealso `DMPlexGetVertexNumbering()`
8311 @*/
8312 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8313 {
8314   DM_Plex *mesh = (DM_Plex *)dm->data;
8315 
8316   PetscFunctionBegin;
8317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8318   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8319   *globalCellNumbers = mesh->globalCellNumbers;
8320   PetscFunctionReturn(0);
8321 }
8322 
8323 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8324 {
8325   PetscInt vStart, vEnd;
8326 
8327   PetscFunctionBegin;
8328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8329   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8330   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8331   PetscFunctionReturn(0);
8332 }
8333 
8334 /*@
8335   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8336 
8337   Input Parameter:
8338 . dm   - The DMPlex object
8339 
8340   Output Parameter:
8341 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8342 
8343   Level: developer
8344 
8345 .seealso `DMPlexGetCellNumbering()`
8346 @*/
8347 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8348 {
8349   DM_Plex *mesh = (DM_Plex *)dm->data;
8350 
8351   PetscFunctionBegin;
8352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8353   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8354   *globalVertexNumbers = mesh->globalVertexNumbers;
8355   PetscFunctionReturn(0);
8356 }
8357 
8358 /*@
8359   DMPlexCreatePointNumbering - Create a global numbering for all points.
8360 
8361   Collective on dm
8362 
8363   Input Parameter:
8364 . dm   - The DMPlex object
8365 
8366   Output Parameter:
8367 . globalPointNumbers - Global numbers for all points on this process
8368 
8369   Notes:
8370 
8371   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8372   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8373   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8374   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8375 
8376   The partitioned mesh is
8377 ```
8378  (2)--0--(3)--1--(4)    (1)--0--(2)
8379 ```
8380   and its global numbering is
8381 ```
8382   (3)--0--(4)--1--(5)--2--(6)
8383 ```
8384   Then the global numbering is provided as
8385 ```
8386 [0] Number of indices in set 5
8387 [0] 0 0
8388 [0] 1 1
8389 [0] 2 3
8390 [0] 3 4
8391 [0] 4 -6
8392 [1] Number of indices in set 3
8393 [1] 0 2
8394 [1] 1 5
8395 [1] 2 6
8396 ```
8397 
8398   Level: developer
8399 
8400 .seealso `DMPlexGetCellNumbering()`
8401 @*/
8402 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8403 {
8404   IS        nums[4];
8405   PetscInt  depths[4], gdepths[4], starts[4];
8406   PetscInt  depth, d, shift = 0;
8407   PetscBool empty = PETSC_FALSE;
8408 
8409   PetscFunctionBegin;
8410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8411   PetscCall(DMPlexGetDepth(dm, &depth));
8412   // For unstratified meshes use dim instead of depth
8413   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8414   // If any stratum is empty, we must mark all empty
8415   for (d = 0; d <= depth; ++d) {
8416     PetscInt end;
8417 
8418     depths[d] = depth - d;
8419     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8420     if (!(starts[d] - end)) empty = PETSC_TRUE;
8421   }
8422   if (empty)
8423     for (d = 0; d <= depth; ++d) {
8424       depths[d] = -1;
8425       starts[d] = -1;
8426     }
8427   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8428   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8429   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]);
8430   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8431   for (d = 0; d <= depth; ++d) {
8432     PetscInt pStart, pEnd, gsize;
8433 
8434     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8435     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8436     shift += gsize;
8437   }
8438   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8439   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8440   PetscFunctionReturn(0);
8441 }
8442 
8443 /*@
8444   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8445 
8446   Input Parameter:
8447 . dm - The DMPlex object
8448 
8449   Output Parameter:
8450 . ranks - The rank field
8451 
8452   Options Database Keys:
8453 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8454 
8455   Level: intermediate
8456 
8457 .seealso: `DMView()`
8458 @*/
8459 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8460 {
8461   DM             rdm;
8462   PetscFE        fe;
8463   PetscScalar   *r;
8464   PetscMPIInt    rank;
8465   DMPolytopeType ct;
8466   PetscInt       dim, cStart, cEnd, c;
8467   PetscBool      simplex;
8468 
8469   PetscFunctionBeginUser;
8470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8471   PetscValidPointer(ranks, 2);
8472   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8473   PetscCall(DMClone(dm, &rdm));
8474   PetscCall(DMGetDimension(rdm, &dim));
8475   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8476   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8477   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8478   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8479   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8480   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8481   PetscCall(PetscFEDestroy(&fe));
8482   PetscCall(DMCreateDS(rdm));
8483   PetscCall(DMCreateGlobalVector(rdm, ranks));
8484   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8485   PetscCall(VecGetArray(*ranks, &r));
8486   for (c = cStart; c < cEnd; ++c) {
8487     PetscScalar *lr;
8488 
8489     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8490     if (lr) *lr = rank;
8491   }
8492   PetscCall(VecRestoreArray(*ranks, &r));
8493   PetscCall(DMDestroy(&rdm));
8494   PetscFunctionReturn(0);
8495 }
8496 
8497 /*@
8498   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8499 
8500   Input Parameters:
8501 + dm    - The DMPlex
8502 - label - The DMLabel
8503 
8504   Output Parameter:
8505 . val - The label value field
8506 
8507   Options Database Keys:
8508 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8509 
8510   Level: intermediate
8511 
8512 .seealso: `DMView()`
8513 @*/
8514 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8515 {
8516   DM           rdm;
8517   PetscFE      fe;
8518   PetscScalar *v;
8519   PetscInt     dim, cStart, cEnd, c;
8520 
8521   PetscFunctionBeginUser;
8522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8523   PetscValidPointer(label, 2);
8524   PetscValidPointer(val, 3);
8525   PetscCall(DMClone(dm, &rdm));
8526   PetscCall(DMGetDimension(rdm, &dim));
8527   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8528   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8529   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8530   PetscCall(PetscFEDestroy(&fe));
8531   PetscCall(DMCreateDS(rdm));
8532   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8533   PetscCall(DMCreateGlobalVector(rdm, val));
8534   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8535   PetscCall(VecGetArray(*val, &v));
8536   for (c = cStart; c < cEnd; ++c) {
8537     PetscScalar *lv;
8538     PetscInt     cval;
8539 
8540     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8541     PetscCall(DMLabelGetValue(label, c, &cval));
8542     *lv = cval;
8543   }
8544   PetscCall(VecRestoreArray(*val, &v));
8545   PetscCall(DMDestroy(&rdm));
8546   PetscFunctionReturn(0);
8547 }
8548 
8549 /*@
8550   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8551 
8552   Input Parameter:
8553 . dm - The DMPlex object
8554 
8555   Notes:
8556   This is a useful diagnostic when creating meshes programmatically.
8557 
8558   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8559 
8560   Level: developer
8561 
8562 .seealso: `DMCreate()`, `DMSetFromOptions()`
8563 @*/
8564 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8565 {
8566   PetscSection    coneSection, supportSection;
8567   const PetscInt *cone, *support;
8568   PetscInt        coneSize, c, supportSize, s;
8569   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8570   PetscBool       storagecheck = PETSC_TRUE;
8571 
8572   PetscFunctionBegin;
8573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8574   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8575   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8576   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8577   /* Check that point p is found in the support of its cone points, and vice versa */
8578   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8579   for (p = pStart; p < pEnd; ++p) {
8580     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8581     PetscCall(DMPlexGetCone(dm, p, &cone));
8582     for (c = 0; c < coneSize; ++c) {
8583       PetscBool dup = PETSC_FALSE;
8584       PetscInt  d;
8585       for (d = c - 1; d >= 0; --d) {
8586         if (cone[c] == cone[d]) {
8587           dup = PETSC_TRUE;
8588           break;
8589         }
8590       }
8591       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8592       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8593       for (s = 0; s < supportSize; ++s) {
8594         if (support[s] == p) break;
8595       }
8596       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8597         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8598         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8599         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8600         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8601         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8602         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8603         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]);
8604         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8605       }
8606     }
8607     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8608     if (p != pp) {
8609       storagecheck = PETSC_FALSE;
8610       continue;
8611     }
8612     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8613     PetscCall(DMPlexGetSupport(dm, p, &support));
8614     for (s = 0; s < supportSize; ++s) {
8615       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8616       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8617       for (c = 0; c < coneSize; ++c) {
8618         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8619         if (cone[c] != pp) {
8620           c = 0;
8621           break;
8622         }
8623         if (cone[c] == p) break;
8624       }
8625       if (c >= coneSize) {
8626         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8627         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8628         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8629         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8630         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8631         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8632         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8633       }
8634     }
8635   }
8636   if (storagecheck) {
8637     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8638     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8639     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8640   }
8641   PetscFunctionReturn(0);
8642 }
8643 
8644 /*
8645   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.
8646 */
8647 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8648 {
8649   DMPolytopeType  cct;
8650   PetscInt        ptpoints[4];
8651   const PetscInt *cone, *ccone, *ptcone;
8652   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8653 
8654   PetscFunctionBegin;
8655   *unsplit = 0;
8656   switch (ct) {
8657   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8658     ptpoints[npt++] = c;
8659     break;
8660   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8661     PetscCall(DMPlexGetCone(dm, c, &cone));
8662     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8663     for (cp = 0; cp < coneSize; ++cp) {
8664       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8665       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8666     }
8667     break;
8668   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8669   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8670     PetscCall(DMPlexGetCone(dm, c, &cone));
8671     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8672     for (cp = 0; cp < coneSize; ++cp) {
8673       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8674       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8675       for (ccp = 0; ccp < cconeSize; ++ccp) {
8676         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8677         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8678           PetscInt p;
8679           for (p = 0; p < npt; ++p)
8680             if (ptpoints[p] == ccone[ccp]) break;
8681           if (p == npt) ptpoints[npt++] = ccone[ccp];
8682         }
8683       }
8684     }
8685     break;
8686   default:
8687     break;
8688   }
8689   for (pt = 0; pt < npt; ++pt) {
8690     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8691     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8692   }
8693   PetscFunctionReturn(0);
8694 }
8695 
8696 /*@
8697   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8698 
8699   Input Parameters:
8700 + dm - The DMPlex object
8701 - cellHeight - Normally 0
8702 
8703   Notes:
8704   This is a useful diagnostic when creating meshes programmatically.
8705   Currently applicable only to homogeneous simplex or tensor meshes.
8706 
8707   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8708 
8709   Level: developer
8710 
8711 .seealso: `DMCreate()`, `DMSetFromOptions()`
8712 @*/
8713 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8714 {
8715   DMPlexInterpolatedFlag interp;
8716   DMPolytopeType         ct;
8717   PetscInt               vStart, vEnd, cStart, cEnd, c;
8718 
8719   PetscFunctionBegin;
8720   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8721   PetscCall(DMPlexIsInterpolated(dm, &interp));
8722   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8723   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8724   for (c = cStart; c < cEnd; ++c) {
8725     PetscInt *closure = NULL;
8726     PetscInt  coneSize, closureSize, cl, Nv = 0;
8727 
8728     PetscCall(DMPlexGetCellType(dm, c, &ct));
8729     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8730     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8731     if (interp == DMPLEX_INTERPOLATED_FULL) {
8732       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8733       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));
8734     }
8735     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8736     for (cl = 0; cl < closureSize * 2; cl += 2) {
8737       const PetscInt p = closure[cl];
8738       if ((p >= vStart) && (p < vEnd)) ++Nv;
8739     }
8740     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8741     /* Special Case: Tensor faces with identified vertices */
8742     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8743       PetscInt unsplit;
8744 
8745       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8746       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8747     }
8748     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));
8749   }
8750   PetscFunctionReturn(0);
8751 }
8752 
8753 /*@
8754   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8755 
8756   Collective
8757 
8758   Input Parameters:
8759 + dm - The DMPlex object
8760 - cellHeight - Normally 0
8761 
8762   Notes:
8763   This is a useful diagnostic when creating meshes programmatically.
8764   This routine is only relevant for meshes that are fully interpolated across all ranks.
8765   It will error out if a partially interpolated mesh is given on some rank.
8766   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8767 
8768   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8769 
8770   Level: developer
8771 
8772 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8773 @*/
8774 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8775 {
8776   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8777   DMPlexInterpolatedFlag interpEnum;
8778 
8779   PetscFunctionBegin;
8780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8781   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8782   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8783   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8784     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8785     PetscFunctionReturn(0);
8786   }
8787 
8788   PetscCall(DMGetDimension(dm, &dim));
8789   PetscCall(DMPlexGetDepth(dm, &depth));
8790   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8791   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8792     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8793     for (c = cStart; c < cEnd; ++c) {
8794       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8795       const DMPolytopeType *faceTypes;
8796       DMPolytopeType        ct;
8797       PetscInt              numFaces, coneSize, f;
8798       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8799 
8800       PetscCall(DMPlexGetCellType(dm, c, &ct));
8801       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8802       if (unsplit) continue;
8803       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8804       PetscCall(DMPlexGetCone(dm, c, &cone));
8805       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8806       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8807       for (cl = 0; cl < closureSize * 2; cl += 2) {
8808         const PetscInt p = closure[cl];
8809         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8810       }
8811       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8812       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);
8813       for (f = 0; f < numFaces; ++f) {
8814         DMPolytopeType fct;
8815         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8816 
8817         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8818         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8819         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8820           const PetscInt p = fclosure[cl];
8821           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8822         }
8823         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]);
8824         for (v = 0; v < fnumCorners; ++v) {
8825           if (fclosure[v] != faces[fOff + v]) {
8826             PetscInt v1;
8827 
8828             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8829             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8830             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8831             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8832             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8833             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]);
8834           }
8835         }
8836         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8837         fOff += faceSizes[f];
8838       }
8839       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8840       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8841     }
8842   }
8843   PetscFunctionReturn(0);
8844 }
8845 
8846 /*@
8847   DMPlexCheckGeometry - Check the geometry of mesh cells
8848 
8849   Input Parameter:
8850 . dm - The DMPlex object
8851 
8852   Notes:
8853   This is a useful diagnostic when creating meshes programmatically.
8854 
8855   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8856 
8857   Level: developer
8858 
8859 .seealso: `DMCreate()`, `DMSetFromOptions()`
8860 @*/
8861 PetscErrorCode DMPlexCheckGeometry(DM dm)
8862 {
8863   Vec       coordinates;
8864   PetscReal detJ, J[9], refVol = 1.0;
8865   PetscReal vol;
8866   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8867 
8868   PetscFunctionBegin;
8869   PetscCall(DMGetDimension(dm, &dim));
8870   PetscCall(DMGetCoordinateDim(dm, &dE));
8871   if (dim != dE) PetscFunctionReturn(0);
8872   PetscCall(DMPlexGetDepth(dm, &depth));
8873   for (d = 0; d < dim; ++d) refVol *= 2.0;
8874   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8875   /* Make sure local coordinates are created, because that step is collective */
8876   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8877   for (c = cStart; c < cEnd; ++c) {
8878     DMPolytopeType ct;
8879     PetscInt       unsplit;
8880     PetscBool      ignoreZeroVol = PETSC_FALSE;
8881 
8882     PetscCall(DMPlexGetCellType(dm, c, &ct));
8883     switch (ct) {
8884     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8885     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8886     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8887       ignoreZeroVol = PETSC_TRUE;
8888       break;
8889     default:
8890       break;
8891     }
8892     switch (ct) {
8893     case DM_POLYTOPE_TRI_PRISM:
8894     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8895     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8896     case DM_POLYTOPE_PYRAMID:
8897       continue;
8898     default:
8899       break;
8900     }
8901     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8902     if (unsplit) continue;
8903     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8904     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);
8905     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8906     /* This should work with periodicity since DG coordinates should be used */
8907     if (depth > 1) {
8908       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8909       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);
8910       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8911     }
8912   }
8913   PetscFunctionReturn(0);
8914 }
8915 
8916 /*@
8917   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8918 
8919   Collective
8920 
8921   Input Parameters:
8922 + dm - The DMPlex object
8923 . pointSF - The Point SF, or NULL for Point SF attached to DM
8924 - allowExtraRoots - Flag to allow extra points not present in the DM
8925 
8926   Notes:
8927   This is mainly intended for debugging/testing purposes.
8928 
8929   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8930 
8931   Extra roots can come from priodic cuts, where additional points appear on the boundary
8932 
8933   Level: developer
8934 
8935 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8936 @*/
8937 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8938 {
8939   PetscInt           l, nleaves, nroots, overlap;
8940   const PetscInt    *locals;
8941   const PetscSFNode *remotes;
8942   PetscBool          distributed;
8943   MPI_Comm           comm;
8944   PetscMPIInt        rank;
8945 
8946   PetscFunctionBegin;
8947   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8948   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8949   else pointSF = dm->sf;
8950   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8951   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8952   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8953   {
8954     PetscMPIInt mpiFlag;
8955 
8956     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8957     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8958   }
8959   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8960   PetscCall(DMPlexIsDistributed(dm, &distributed));
8961   if (!distributed) {
8962     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);
8963     PetscFunctionReturn(0);
8964   }
8965   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);
8966   PetscCall(DMPlexGetOverlap(dm, &overlap));
8967 
8968   /* Check SF graph is compatible with DMPlex chart */
8969   {
8970     PetscInt pStart, pEnd, maxLeaf;
8971 
8972     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8973     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8974     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8975     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8976   }
8977 
8978   /* Check Point SF has no local points referenced */
8979   for (l = 0; l < nleaves; l++) {
8980     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);
8981   }
8982 
8983   /* Check there are no cells in interface */
8984   if (!overlap) {
8985     PetscInt cellHeight, cStart, cEnd;
8986 
8987     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8988     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8989     for (l = 0; l < nleaves; ++l) {
8990       const PetscInt point = locals ? locals[l] : l;
8991 
8992       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8993     }
8994   }
8995 
8996   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8997   {
8998     const PetscInt *rootdegree;
8999 
9000     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9001     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9002     for (l = 0; l < nleaves; ++l) {
9003       const PetscInt  point = locals ? locals[l] : l;
9004       const PetscInt *cone;
9005       PetscInt        coneSize, c, idx;
9006 
9007       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9008       PetscCall(DMPlexGetCone(dm, point, &cone));
9009       for (c = 0; c < coneSize; ++c) {
9010         if (!rootdegree[cone[c]]) {
9011           if (locals) {
9012             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9013           } else {
9014             idx = (cone[c] < nleaves) ? cone[c] : -1;
9015           }
9016           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9017         }
9018       }
9019     }
9020   }
9021   PetscFunctionReturn(0);
9022 }
9023 
9024 /*@
9025   DMPlexCheck - Perform various checks of Plex sanity
9026 
9027   Input Parameter:
9028 . dm - The DMPlex object
9029 
9030   Notes:
9031   This is a useful diagnostic when creating meshes programmatically.
9032 
9033   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9034 
9035   Currently does not include DMPlexCheckCellShape().
9036 
9037   Level: developer
9038 
9039 .seealso: DMCreate(), DMSetFromOptions()
9040 @*/
9041 PetscErrorCode DMPlexCheck(DM dm)
9042 {
9043   PetscInt cellHeight;
9044 
9045   PetscFunctionBegin;
9046   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9047   PetscCall(DMPlexCheckSymmetry(dm));
9048   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9049   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9050   PetscCall(DMPlexCheckGeometry(dm));
9051   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9052   PetscCall(DMPlexCheckInterfaceCones(dm));
9053   PetscFunctionReturn(0);
9054 }
9055 
9056 typedef struct cell_stats {
9057   PetscReal min, max, sum, squaresum;
9058   PetscInt  count;
9059 } cell_stats_t;
9060 
9061 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9062 {
9063   PetscInt i, N = *len;
9064 
9065   for (i = 0; i < N; i++) {
9066     cell_stats_t *A = (cell_stats_t *)a;
9067     cell_stats_t *B = (cell_stats_t *)b;
9068 
9069     B->min = PetscMin(A->min, B->min);
9070     B->max = PetscMax(A->max, B->max);
9071     B->sum += A->sum;
9072     B->squaresum += A->squaresum;
9073     B->count += A->count;
9074   }
9075 }
9076 
9077 /*@
9078   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9079 
9080   Collective on dm
9081 
9082   Input Parameters:
9083 + dm        - The DMPlex object
9084 . output    - If true, statistics will be displayed on stdout
9085 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
9086 
9087   Notes:
9088   This is mainly intended for debugging/testing purposes.
9089 
9090   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9091 
9092   Level: developer
9093 
9094 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9095 @*/
9096 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9097 {
9098   DM           dmCoarse;
9099   cell_stats_t stats, globalStats;
9100   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9101   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9102   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9103   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9104   PetscMPIInt  rank, size;
9105 
9106   PetscFunctionBegin;
9107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9108   stats.min = PETSC_MAX_REAL;
9109   stats.max = PETSC_MIN_REAL;
9110   stats.sum = stats.squaresum = 0.;
9111   stats.count                 = 0;
9112 
9113   PetscCallMPI(MPI_Comm_size(comm, &size));
9114   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9115   PetscCall(DMGetCoordinateDim(dm, &cdim));
9116   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9118   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9119   for (c = cStart; c < cEnd; c++) {
9120     PetscInt  i;
9121     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9122 
9123     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9124     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9125     for (i = 0; i < PetscSqr(cdim); ++i) {
9126       frobJ += J[i] * J[i];
9127       frobInvJ += invJ[i] * invJ[i];
9128     }
9129     cond2 = frobJ * frobInvJ;
9130     cond  = PetscSqrtReal(cond2);
9131 
9132     stats.min = PetscMin(stats.min, cond);
9133     stats.max = PetscMax(stats.max, cond);
9134     stats.sum += cond;
9135     stats.squaresum += cond2;
9136     stats.count++;
9137     if (output && cond > limit) {
9138       PetscSection coordSection;
9139       Vec          coordsLocal;
9140       PetscScalar *coords = NULL;
9141       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9142 
9143       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9144       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9145       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9146       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9147       for (i = 0; i < Nv / cdim; ++i) {
9148         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9149         for (d = 0; d < cdim; ++d) {
9150           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9151           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9152         }
9153         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9154       }
9155       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9156       for (cl = 0; cl < clSize * 2; cl += 2) {
9157         const PetscInt edge = closure[cl];
9158 
9159         if ((edge >= eStart) && (edge < eEnd)) {
9160           PetscReal len;
9161 
9162           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9163           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9164         }
9165       }
9166       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9167       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9168     }
9169   }
9170   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9171 
9172   if (size > 1) {
9173     PetscMPIInt  blockLengths[2] = {4, 1};
9174     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9175     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9176     MPI_Op       statReduce;
9177 
9178     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9179     PetscCallMPI(MPI_Type_commit(&statType));
9180     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9181     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9182     PetscCallMPI(MPI_Op_free(&statReduce));
9183     PetscCallMPI(MPI_Type_free(&statType));
9184   } else {
9185     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9186   }
9187   if (rank == 0) {
9188     count = globalStats.count;
9189     min   = globalStats.min;
9190     max   = globalStats.max;
9191     mean  = globalStats.sum / globalStats.count;
9192     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9193   }
9194 
9195   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));
9196   PetscCall(PetscFree2(J, invJ));
9197 
9198   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9199   if (dmCoarse) {
9200     PetscBool isplex;
9201 
9202     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9203     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9204   }
9205   PetscFunctionReturn(0);
9206 }
9207 
9208 /*@
9209   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9210   orthogonal quality below given tolerance.
9211 
9212   Collective on dm
9213 
9214   Input Parameters:
9215 + dm   - The DMPlex object
9216 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
9217 - atol - [0, 1] Absolute tolerance for tagging cells.
9218 
9219   Output Parameters:
9220 + OrthQual      - Vec containing orthogonal quality per cell
9221 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
9222 
9223   Options Database Keys:
9224 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
9225 supported.
9226 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9227 
9228   Notes:
9229   Orthogonal quality is given by the following formula:
9230 
9231   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
9232 
9233   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
9234   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9235   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9236   calculating the cosine of the angle between these vectors.
9237 
9238   Orthogonal quality ranges from 1 (best) to 0 (worst).
9239 
9240   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
9241   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9242 
9243   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9244 
9245   Level: intermediate
9246 
9247 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
9248 @*/
9249 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9250 {
9251   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9252   PetscInt              *idx;
9253   PetscScalar           *oqVals;
9254   const PetscScalar     *cellGeomArr, *faceGeomArr;
9255   PetscReal             *ci, *fi, *Ai;
9256   MPI_Comm               comm;
9257   Vec                    cellgeom, facegeom;
9258   DM                     dmFace, dmCell;
9259   IS                     glob;
9260   ISLocalToGlobalMapping ltog;
9261   PetscViewer            vwr;
9262 
9263   PetscFunctionBegin;
9264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9265   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9266   PetscValidPointer(OrthQual, 4);
9267   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9268   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9269   PetscCall(DMGetDimension(dm, &nc));
9270   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9271   {
9272     DMPlexInterpolatedFlag interpFlag;
9273 
9274     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9275     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9276       PetscMPIInt rank;
9277 
9278       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9279       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9280     }
9281   }
9282   if (OrthQualLabel) {
9283     PetscValidPointer(OrthQualLabel, 5);
9284     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9285     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9286   } else {
9287     *OrthQualLabel = NULL;
9288   }
9289   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9290   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9291   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9292   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9293   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9294   PetscCall(VecCreate(comm, OrthQual));
9295   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9296   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9297   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9298   PetscCall(VecSetUp(*OrthQual));
9299   PetscCall(ISDestroy(&glob));
9300   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9301   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9302   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9303   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9304   PetscCall(VecGetDM(cellgeom, &dmCell));
9305   PetscCall(VecGetDM(facegeom, &dmFace));
9306   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9307   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9308     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9309     PetscInt         cellarr[2], *adj = NULL;
9310     PetscScalar     *cArr, *fArr;
9311     PetscReal        minvalc = 1.0, minvalf = 1.0;
9312     PetscFVCellGeom *cg;
9313 
9314     idx[cellIter] = cell - cStart;
9315     cellarr[0]    = cell;
9316     /* Make indexing into cellGeom easier */
9317     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9318     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9319     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9320     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9321     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9322       PetscInt         i;
9323       const PetscInt   neigh  = adj[cellneigh];
9324       PetscReal        normci = 0, normfi = 0, normai = 0;
9325       PetscFVCellGeom *cgneigh;
9326       PetscFVFaceGeom *fg;
9327 
9328       /* Don't count ourselves in the neighbor list */
9329       if (neigh == cell) continue;
9330       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9331       cellarr[1] = neigh;
9332       {
9333         PetscInt        numcovpts;
9334         const PetscInt *covpts;
9335 
9336         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9337         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9338         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9339       }
9340 
9341       /* Compute c_i, f_i and their norms */
9342       for (i = 0; i < nc; i++) {
9343         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9344         fi[i] = fg->centroid[i] - cg->centroid[i];
9345         Ai[i] = fg->normal[i];
9346         normci += PetscPowReal(ci[i], 2);
9347         normfi += PetscPowReal(fi[i], 2);
9348         normai += PetscPowReal(Ai[i], 2);
9349       }
9350       normci = PetscSqrtReal(normci);
9351       normfi = PetscSqrtReal(normfi);
9352       normai = PetscSqrtReal(normai);
9353 
9354       /* Normalize and compute for each face-cell-normal pair */
9355       for (i = 0; i < nc; i++) {
9356         ci[i] = ci[i] / normci;
9357         fi[i] = fi[i] / normfi;
9358         Ai[i] = Ai[i] / normai;
9359         /* PetscAbs because I don't know if normals are guaranteed to point out */
9360         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9361         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9362       }
9363       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9364       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9365     }
9366     PetscCall(PetscFree(adj));
9367     PetscCall(PetscFree2(cArr, fArr));
9368     /* Defer to cell if they're equal */
9369     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9370     if (OrthQualLabel) {
9371       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9372     }
9373   }
9374   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9375   PetscCall(VecAssemblyBegin(*OrthQual));
9376   PetscCall(VecAssemblyEnd(*OrthQual));
9377   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9378   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9379   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9380   if (OrthQualLabel) {
9381     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9382   }
9383   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9384   PetscCall(PetscViewerDestroy(&vwr));
9385   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9386   PetscFunctionReturn(0);
9387 }
9388 
9389 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9390  * interpolator construction */
9391 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9392 {
9393   PetscSection section, newSection, gsection;
9394   PetscSF      sf;
9395   PetscBool    hasConstraints, ghasConstraints;
9396 
9397   PetscFunctionBegin;
9398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9399   PetscValidPointer(odm, 2);
9400   PetscCall(DMGetLocalSection(dm, &section));
9401   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9402   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9403   if (!ghasConstraints) {
9404     PetscCall(PetscObjectReference((PetscObject)dm));
9405     *odm = dm;
9406     PetscFunctionReturn(0);
9407   }
9408   PetscCall(DMClone(dm, odm));
9409   PetscCall(DMCopyFields(dm, *odm));
9410   PetscCall(DMGetLocalSection(*odm, &newSection));
9411   PetscCall(DMGetPointSF(*odm, &sf));
9412   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9413   PetscCall(DMSetGlobalSection(*odm, gsection));
9414   PetscCall(PetscSectionDestroy(&gsection));
9415   PetscFunctionReturn(0);
9416 }
9417 
9418 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9419 {
9420   DM        dmco, dmfo;
9421   Mat       interpo;
9422   Vec       rscale;
9423   Vec       cglobalo, clocal;
9424   Vec       fglobal, fglobalo, flocal;
9425   PetscBool regular;
9426 
9427   PetscFunctionBegin;
9428   PetscCall(DMGetFullDM(dmc, &dmco));
9429   PetscCall(DMGetFullDM(dmf, &dmfo));
9430   PetscCall(DMSetCoarseDM(dmfo, dmco));
9431   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9432   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9433   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9434   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9435   PetscCall(DMCreateLocalVector(dmc, &clocal));
9436   PetscCall(VecSet(cglobalo, 0.));
9437   PetscCall(VecSet(clocal, 0.));
9438   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9439   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9440   PetscCall(DMCreateLocalVector(dmf, &flocal));
9441   PetscCall(VecSet(fglobal, 0.));
9442   PetscCall(VecSet(fglobalo, 0.));
9443   PetscCall(VecSet(flocal, 0.));
9444   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9445   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9446   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9447   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9448   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9449   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9450   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9451   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9452   *shift = fglobal;
9453   PetscCall(VecDestroy(&flocal));
9454   PetscCall(VecDestroy(&fglobalo));
9455   PetscCall(VecDestroy(&clocal));
9456   PetscCall(VecDestroy(&cglobalo));
9457   PetscCall(VecDestroy(&rscale));
9458   PetscCall(MatDestroy(&interpo));
9459   PetscCall(DMDestroy(&dmfo));
9460   PetscCall(DMDestroy(&dmco));
9461   PetscFunctionReturn(0);
9462 }
9463 
9464 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9465 {
9466   PetscObject shifto;
9467   Vec         shift;
9468 
9469   PetscFunctionBegin;
9470   if (!interp) {
9471     Vec rscale;
9472 
9473     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9474     PetscCall(VecDestroy(&rscale));
9475   } else {
9476     PetscCall(PetscObjectReference((PetscObject)interp));
9477   }
9478   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9479   if (!shifto) {
9480     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9481     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9482     shifto = (PetscObject)shift;
9483     PetscCall(VecDestroy(&shift));
9484   }
9485   shift = (Vec)shifto;
9486   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9487   PetscCall(VecAXPY(fineSol, 1.0, shift));
9488   PetscCall(MatDestroy(&interp));
9489   PetscFunctionReturn(0);
9490 }
9491 
9492 /* Pointwise interpolation
9493      Just code FEM for now
9494      u^f = I u^c
9495      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9496      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9497      I_{ij} = psi^f_i phi^c_j
9498 */
9499 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9500 {
9501   PetscSection gsc, gsf;
9502   PetscInt     m, n;
9503   void        *ctx;
9504   DM           cdm;
9505   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9506 
9507   PetscFunctionBegin;
9508   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9509   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9510   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9511   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9512 
9513   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9514   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9515   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9516   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9517   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9518 
9519   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9520   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9521   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9522   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9523   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9524   if (scaling) {
9525     /* Use naive scaling */
9526     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9527   }
9528   PetscFunctionReturn(0);
9529 }
9530 
9531 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9532 {
9533   VecScatter ctx;
9534 
9535   PetscFunctionBegin;
9536   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9537   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9538   PetscCall(VecScatterDestroy(&ctx));
9539   PetscFunctionReturn(0);
9540 }
9541 
9542 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[])
9543 {
9544   const PetscInt Nc = uOff[1] - uOff[0];
9545   PetscInt       c;
9546   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9547 }
9548 
9549 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9550 {
9551   DM           dmc;
9552   PetscDS      ds;
9553   Vec          ones, locmass;
9554   IS           cellIS;
9555   PetscFormKey key;
9556   PetscInt     depth;
9557 
9558   PetscFunctionBegin;
9559   PetscCall(DMClone(dm, &dmc));
9560   PetscCall(DMCopyDisc(dm, dmc));
9561   PetscCall(DMGetDS(dmc, &ds));
9562   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9563   PetscCall(DMCreateGlobalVector(dmc, mass));
9564   PetscCall(DMGetLocalVector(dmc, &ones));
9565   PetscCall(DMGetLocalVector(dmc, &locmass));
9566   PetscCall(DMPlexGetDepth(dmc, &depth));
9567   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9568   PetscCall(VecSet(locmass, 0.0));
9569   PetscCall(VecSet(ones, 1.0));
9570   key.label = NULL;
9571   key.value = 0;
9572   key.field = 0;
9573   key.part  = 0;
9574   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9575   PetscCall(ISDestroy(&cellIS));
9576   PetscCall(VecSet(*mass, 0.0));
9577   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9578   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9579   PetscCall(DMRestoreLocalVector(dmc, &ones));
9580   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9581   PetscCall(DMDestroy(&dmc));
9582   PetscFunctionReturn(0);
9583 }
9584 
9585 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9586 {
9587   PetscSection gsc, gsf;
9588   PetscInt     m, n;
9589   void        *ctx;
9590   DM           cdm;
9591   PetscBool    regular;
9592 
9593   PetscFunctionBegin;
9594   if (dmFine == dmCoarse) {
9595     DM            dmc;
9596     PetscDS       ds;
9597     PetscWeakForm wf;
9598     Vec           u;
9599     IS            cellIS;
9600     PetscFormKey  key;
9601     PetscInt      depth;
9602 
9603     PetscCall(DMClone(dmFine, &dmc));
9604     PetscCall(DMCopyDisc(dmFine, dmc));
9605     PetscCall(DMGetDS(dmc, &ds));
9606     PetscCall(PetscDSGetWeakForm(ds, &wf));
9607     PetscCall(PetscWeakFormClear(wf));
9608     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9609     PetscCall(DMCreateMatrix(dmc, mass));
9610     PetscCall(DMGetLocalVector(dmc, &u));
9611     PetscCall(DMPlexGetDepth(dmc, &depth));
9612     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9613     PetscCall(MatZeroEntries(*mass));
9614     key.label = NULL;
9615     key.value = 0;
9616     key.field = 0;
9617     key.part  = 0;
9618     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9619     PetscCall(ISDestroy(&cellIS));
9620     PetscCall(DMRestoreLocalVector(dmc, &u));
9621     PetscCall(DMDestroy(&dmc));
9622   } else {
9623     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9624     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9625     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9626     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9627 
9628     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9629     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9630     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9631     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9632 
9633     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9634     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9635     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9636     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9637   }
9638   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9639   PetscFunctionReturn(0);
9640 }
9641 
9642 /*@
9643   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9644 
9645   Input Parameter:
9646 . dm - The DMPlex object
9647 
9648   Output Parameter:
9649 . regular - The flag
9650 
9651   Level: intermediate
9652 
9653 .seealso: `DMPlexSetRegularRefinement()`
9654 @*/
9655 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9656 {
9657   PetscFunctionBegin;
9658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9659   PetscValidBoolPointer(regular, 2);
9660   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9661   PetscFunctionReturn(0);
9662 }
9663 
9664 /*@
9665   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9666 
9667   Input Parameters:
9668 + dm - The DMPlex object
9669 - regular - The flag
9670 
9671   Level: intermediate
9672 
9673 .seealso: `DMPlexGetRegularRefinement()`
9674 @*/
9675 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9676 {
9677   PetscFunctionBegin;
9678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9679   ((DM_Plex *)dm->data)->regularRefinement = regular;
9680   PetscFunctionReturn(0);
9681 }
9682 
9683 /* anchors */
9684 /*@
9685   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9686   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9687 
9688   not collective
9689 
9690   Input Parameter:
9691 . dm - The DMPlex object
9692 
9693   Output Parameters:
9694 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9695 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9696 
9697   Level: intermediate
9698 
9699 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9700 @*/
9701 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9702 {
9703   DM_Plex *plex = (DM_Plex *)dm->data;
9704 
9705   PetscFunctionBegin;
9706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9707   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9708   if (anchorSection) *anchorSection = plex->anchorSection;
9709   if (anchorIS) *anchorIS = plex->anchorIS;
9710   PetscFunctionReturn(0);
9711 }
9712 
9713 /*@
9714   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9715   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9716   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9717 
9718   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9719   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9720 
9721   collective on dm
9722 
9723   Input Parameters:
9724 + dm - The DMPlex object
9725 . 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).
9726 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9727 
9728   The reference counts of anchorSection and anchorIS are incremented.
9729 
9730   Level: intermediate
9731 
9732 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9733 @*/
9734 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9735 {
9736   DM_Plex    *plex = (DM_Plex *)dm->data;
9737   PetscMPIInt result;
9738 
9739   PetscFunctionBegin;
9740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9741   if (anchorSection) {
9742     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9743     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9744     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9745   }
9746   if (anchorIS) {
9747     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9748     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9749     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9750   }
9751 
9752   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9753   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9754   plex->anchorSection = anchorSection;
9755 
9756   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9757   PetscCall(ISDestroy(&plex->anchorIS));
9758   plex->anchorIS = anchorIS;
9759 
9760   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9761     PetscInt        size, a, pStart, pEnd;
9762     const PetscInt *anchors;
9763 
9764     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9765     PetscCall(ISGetLocalSize(anchorIS, &size));
9766     PetscCall(ISGetIndices(anchorIS, &anchors));
9767     for (a = 0; a < size; a++) {
9768       PetscInt p;
9769 
9770       p = anchors[a];
9771       if (p >= pStart && p < pEnd) {
9772         PetscInt dof;
9773 
9774         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9775         if (dof) {
9776           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9777           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9778         }
9779       }
9780     }
9781     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9782   }
9783   /* reset the generic constraints */
9784   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9785   PetscFunctionReturn(0);
9786 }
9787 
9788 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9789 {
9790   PetscSection anchorSection;
9791   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9792 
9793   PetscFunctionBegin;
9794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9795   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9796   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9797   PetscCall(PetscSectionGetNumFields(section, &numFields));
9798   if (numFields) {
9799     PetscInt f;
9800     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9801 
9802     for (f = 0; f < numFields; f++) {
9803       PetscInt numComp;
9804 
9805       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9806       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9807     }
9808   }
9809   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9810   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9811   pStart = PetscMax(pStart, sStart);
9812   pEnd   = PetscMin(pEnd, sEnd);
9813   pEnd   = PetscMax(pStart, pEnd);
9814   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9815   for (p = pStart; p < pEnd; p++) {
9816     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9817     if (dof) {
9818       PetscCall(PetscSectionGetDof(section, p, &dof));
9819       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9820       for (f = 0; f < numFields; f++) {
9821         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9822         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9823       }
9824     }
9825   }
9826   PetscCall(PetscSectionSetUp(*cSec));
9827   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9828   PetscFunctionReturn(0);
9829 }
9830 
9831 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9832 {
9833   PetscSection    aSec;
9834   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9835   const PetscInt *anchors;
9836   PetscInt        numFields, f;
9837   IS              aIS;
9838   MatType         mtype;
9839   PetscBool       iscuda, iskokkos;
9840 
9841   PetscFunctionBegin;
9842   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9843   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9844   PetscCall(PetscSectionGetStorageSize(section, &n));
9845   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9846   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9847   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9848   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9849   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9850   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9851   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9852   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9853   else mtype = MATSEQAIJ;
9854   PetscCall(MatSetType(*cMat, mtype));
9855   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9856   PetscCall(ISGetIndices(aIS, &anchors));
9857   /* cSec will be a subset of aSec and section */
9858   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9859   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9860   PetscCall(PetscMalloc1(m + 1, &i));
9861   i[0] = 0;
9862   PetscCall(PetscSectionGetNumFields(section, &numFields));
9863   for (p = pStart; p < pEnd; p++) {
9864     PetscInt rDof, rOff, r;
9865 
9866     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9867     if (!rDof) continue;
9868     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9869     if (numFields) {
9870       for (f = 0; f < numFields; f++) {
9871         annz = 0;
9872         for (r = 0; r < rDof; r++) {
9873           a = anchors[rOff + r];
9874           if (a < sStart || a >= sEnd) continue;
9875           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9876           annz += aDof;
9877         }
9878         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9879         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9880         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9881       }
9882     } else {
9883       annz = 0;
9884       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9885       for (q = 0; q < dof; q++) {
9886         a = anchors[rOff + q];
9887         if (a < sStart || a >= sEnd) continue;
9888         PetscCall(PetscSectionGetDof(section, a, &aDof));
9889         annz += aDof;
9890       }
9891       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9892       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9893       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9894     }
9895   }
9896   nnz = i[m];
9897   PetscCall(PetscMalloc1(nnz, &j));
9898   offset = 0;
9899   for (p = pStart; p < pEnd; p++) {
9900     if (numFields) {
9901       for (f = 0; f < numFields; f++) {
9902         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9903         for (q = 0; q < dof; q++) {
9904           PetscInt rDof, rOff, r;
9905           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9906           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9907           for (r = 0; r < rDof; r++) {
9908             PetscInt s;
9909 
9910             a = anchors[rOff + r];
9911             if (a < sStart || a >= sEnd) continue;
9912             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9913             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9914             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9915           }
9916         }
9917       }
9918     } else {
9919       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9920       for (q = 0; q < dof; q++) {
9921         PetscInt rDof, rOff, r;
9922         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9923         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9924         for (r = 0; r < rDof; r++) {
9925           PetscInt s;
9926 
9927           a = anchors[rOff + r];
9928           if (a < sStart || a >= sEnd) continue;
9929           PetscCall(PetscSectionGetDof(section, a, &aDof));
9930           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9931           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9932         }
9933       }
9934     }
9935   }
9936   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9937   PetscCall(PetscFree(i));
9938   PetscCall(PetscFree(j));
9939   PetscCall(ISRestoreIndices(aIS, &anchors));
9940   PetscFunctionReturn(0);
9941 }
9942 
9943 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9944 {
9945   DM_Plex     *plex = (DM_Plex *)dm->data;
9946   PetscSection anchorSection, section, cSec;
9947   Mat          cMat;
9948 
9949   PetscFunctionBegin;
9950   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9951   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9952   if (anchorSection) {
9953     PetscInt Nf;
9954 
9955     PetscCall(DMGetLocalSection(dm, &section));
9956     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9957     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9958     PetscCall(DMGetNumFields(dm, &Nf));
9959     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9960     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9961     PetscCall(PetscSectionDestroy(&cSec));
9962     PetscCall(MatDestroy(&cMat));
9963   }
9964   PetscFunctionReturn(0);
9965 }
9966 
9967 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9968 {
9969   IS           subis;
9970   PetscSection section, subsection;
9971 
9972   PetscFunctionBegin;
9973   PetscCall(DMGetLocalSection(dm, &section));
9974   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9975   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9976   /* Create subdomain */
9977   PetscCall(DMPlexFilter(dm, label, value, subdm));
9978   /* Create submodel */
9979   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9980   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9981   PetscCall(DMSetLocalSection(*subdm, subsection));
9982   PetscCall(PetscSectionDestroy(&subsection));
9983   PetscCall(DMCopyDisc(dm, *subdm));
9984   /* Create map from submodel to global model */
9985   if (is) {
9986     PetscSection    sectionGlobal, subsectionGlobal;
9987     IS              spIS;
9988     const PetscInt *spmap;
9989     PetscInt       *subIndices;
9990     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9991     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9992 
9993     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9994     PetscCall(ISGetIndices(spIS, &spmap));
9995     PetscCall(PetscSectionGetNumFields(section, &Nf));
9996     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9997     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9998     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9999     for (p = pStart; p < pEnd; ++p) {
10000       PetscInt gdof, pSubSize = 0;
10001 
10002       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10003       if (gdof > 0) {
10004         for (f = 0; f < Nf; ++f) {
10005           PetscInt fdof, fcdof;
10006 
10007           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10008           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10009           pSubSize += fdof - fcdof;
10010         }
10011         subSize += pSubSize;
10012         if (pSubSize) {
10013           if (bs < 0) {
10014             bs = pSubSize;
10015           } else if (bs != pSubSize) {
10016             /* Layout does not admit a pointwise block size */
10017             bs = 1;
10018           }
10019         }
10020       }
10021     }
10022     /* Must have same blocksize on all procs (some might have no points) */
10023     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10024     bsLocal[1] = bs;
10025     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10026     if (bsMinMax[0] != bsMinMax[1]) {
10027       bs = 1;
10028     } else {
10029       bs = bsMinMax[0];
10030     }
10031     PetscCall(PetscMalloc1(subSize, &subIndices));
10032     for (p = pStart; p < pEnd; ++p) {
10033       PetscInt gdof, goff;
10034 
10035       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10036       if (gdof > 0) {
10037         const PetscInt point = spmap[p];
10038 
10039         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10040         for (f = 0; f < Nf; ++f) {
10041           PetscInt fdof, fcdof, fc, f2, poff = 0;
10042 
10043           /* Can get rid of this loop by storing field information in the global section */
10044           for (f2 = 0; f2 < f; ++f2) {
10045             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10046             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10047             poff += fdof - fcdof;
10048           }
10049           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10050           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10051           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10052         }
10053       }
10054     }
10055     PetscCall(ISRestoreIndices(spIS, &spmap));
10056     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10057     if (bs > 1) {
10058       /* We need to check that the block size does not come from non-contiguous fields */
10059       PetscInt i, j, set = 1;
10060       for (i = 0; i < subSize; i += bs) {
10061         for (j = 0; j < bs; ++j) {
10062           if (subIndices[i + j] != subIndices[i] + j) {
10063             set = 0;
10064             break;
10065           }
10066         }
10067       }
10068       if (set) PetscCall(ISSetBlockSize(*is, bs));
10069     }
10070     /* Attach nullspace */
10071     for (f = 0; f < Nf; ++f) {
10072       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10073       if ((*subdm)->nullspaceConstructors[f]) break;
10074     }
10075     if (f < Nf) {
10076       MatNullSpace nullSpace;
10077       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10078 
10079       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10080       PetscCall(MatNullSpaceDestroy(&nullSpace));
10081     }
10082   }
10083   PetscFunctionReturn(0);
10084 }
10085 
10086 /*@
10087   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10088 
10089   Input Parameter:
10090 - dm - The DM
10091 
10092   Level: developer
10093 
10094   Options Database Keys:
10095 . -dm_plex_monitor_throughput - Activate the monitor
10096 
10097 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
10098 @*/
10099 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10100 {
10101 #if defined(PETSC_USE_LOG)
10102   PetscStageLog      stageLog;
10103   PetscLogEvent      event;
10104   PetscLogStage      stage;
10105   PetscEventPerfInfo eventInfo;
10106   PetscReal          cellRate, flopRate;
10107   PetscInt           cStart, cEnd, Nf, N;
10108   const char        *name;
10109 #endif
10110 
10111   PetscFunctionBegin;
10112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10113 #if defined(PETSC_USE_LOG)
10114   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10115   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10116   PetscCall(DMGetNumFields(dm, &Nf));
10117   PetscCall(PetscLogGetStageLog(&stageLog));
10118   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10119   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10120   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10121   N        = (cEnd - cStart) * Nf * eventInfo.count;
10122   flopRate = eventInfo.flops / eventInfo.time;
10123   cellRate = N / eventInfo.time;
10124   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)));
10125 #else
10126   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10127 #endif
10128   PetscFunctionReturn(0);
10129 }
10130