xref: /petsc/src/dm/impls/plex/plex.c (revision 4e9ddbc4fd1f64c879c450faa3dd142bba1fa98f)
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, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
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   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           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);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           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]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           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]));
358           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]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       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]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &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(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             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));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       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));
1619       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));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     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));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     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));
1633     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));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     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));
1641     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));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         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));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer     - The `PetscViewer` for saving
1954 - sectiondm  - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   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.
1960 
1961   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.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   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.
2001 
2002   Typical calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2026 @*/
2027 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2028 {
2029   PetscBool ishdf5;
2030 
2031   PetscFunctionBegin;
2032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2033   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2034   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2035   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2036   /* Check consistency */
2037   {
2038     PetscSection section;
2039     PetscBool    includesConstraints;
2040     PetscInt     m, m1;
2041 
2042     PetscCall(VecGetLocalSize(vec, &m1));
2043     PetscCall(DMGetGlobalSection(sectiondm, &section));
2044     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2045     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2046     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2047     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2048   }
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2054 #else
2055     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2056 #endif
2057   }
2058   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2059   PetscFunctionReturn(PETSC_SUCCESS);
2060 }
2061 
2062 /*@
2063   DMPlexLocalVectorView - Saves a local vector
2064 
2065   Collective
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   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.
2077 
2078   Typical calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2101 @*/
2102 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2110   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2111   /* Check consistency */
2112   {
2113     PetscSection section;
2114     PetscBool    includesConstraints;
2115     PetscInt     m, m1;
2116 
2117     PetscCall(VecGetLocalSize(vec, &m1));
2118     PetscCall(DMGetLocalSection(sectiondm, &section));
2119     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2120     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2121     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2122     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2123   }
2124   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2125   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   if (ishdf5) {
2127 #if defined(PETSC_HAVE_HDF5)
2128     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   if (ishdf5) {
2146 #if defined(PETSC_HAVE_HDF5)
2147     PetscViewerFormat format;
2148     PetscCall(PetscViewerGetFormat(viewer, &format));
2149     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2150       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2151     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2152       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2153     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2154     PetscFunctionReturn(PETSC_SUCCESS);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2159 }
2160 
2161 /*@
2162   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm                - The `DM` into which the topology is loaded
2168 - viewer            - The `PetscViewer` for the saved topology
2169 
2170   Output Parameter:
2171 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2172 
2173   Level: advanced
2174 
2175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective
2207 
2208   Input Parameters:
2209 + dm     - The `DM` into which the coordinates are loaded
2210 . viewer - The `PetscViewer` for the saved coordinates
2211 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2212 
2213   Level: advanced
2214 
2215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2216           `PetscSF`, `PetscViewer`
2217 @*/
2218 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2219 {
2220   PetscBool ishdf5;
2221 
2222   PetscFunctionBegin;
2223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2224   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2225   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscViewerFormat format;
2231     PetscCall(PetscViewerGetFormat(viewer, &format));
2232     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2233       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2234     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2235 #else
2236     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2237 #endif
2238   }
2239   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2240   PetscFunctionReturn(PETSC_SUCCESS);
2241 }
2242 
2243 /*@
2244   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2245 
2246   Collective
2247 
2248   Input Parameters:
2249 + dm     - The `DM` into which the labels are loaded
2250 . viewer - The `PetscViewer` for the saved labels
2251 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2252 
2253   Level: advanced
2254 
2255   Note:
2256   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2259           `PetscSF`, `PetscViewer`
2260 @*/
2261 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscViewerFormat format;
2274 
2275     PetscCall(PetscViewerGetFormat(viewer, &format));
2276     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2277       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2278     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2279 #else
2280     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2281 #endif
2282   }
2283   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   PetscFunctionReturn(PETSC_SUCCESS);
2285 }
2286 
2287 /*@
2288   DMPlexSectionLoad - Loads section into a `DMPLEX`
2289 
2290   Collective
2291 
2292   Input Parameters:
2293 + dm          - The `DM` that represents the topology
2294 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2295 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2296 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2297 
2298   Output Parameters
2299 + globalDofSF - The `PetscSF` 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)
2300 - localDofSF  - The `PetscSF` 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)
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   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.
2306 
2307   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.
2308 
2309   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.
2310 
2311   Example using 2 processes:
2312 .vb
2313   NX (number of points on dm): 4
2314   sectionA                   : the on-disk section
2315   vecA                       : a vector associated with sectionA
2316   sectionB                   : sectiondm's local section constructed in this function
2317   vecB (local)               : a vector associated with sectiondm's local section
2318   vecB (global)              : a vector associated with sectiondm's global section
2319 
2320                                      rank 0    rank 1
2321   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2322   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2323   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2324   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2325   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2326   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2327   sectionB->atlasDof             :     1 0 1 | 1 3
2328   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2329   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2330   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2331 .ve
2332   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2346   if (localDofSF) PetscValidPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   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.
2376 
2377   Typical calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2397           `PetscSF`, `PetscViewer`
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(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective
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 `PetscSF` 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 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(PetscFree(mesh->cellTypes));
2537   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2538   PetscCall(PetscFree(mesh->tetgenOpts));
2539   PetscCall(PetscFree(mesh->triangleOpts));
2540   PetscCall(PetscFree(mesh->transformType));
2541   PetscCall(PetscFree(mesh->distributionName));
2542   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2543   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2544   PetscCall(ISDestroy(&mesh->subpointIS));
2545   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2546   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2548   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2549   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2550   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2551   PetscCall(ISDestroy(&mesh->anchorIS));
2552   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2553   PetscCall(PetscFree(mesh->parents));
2554   PetscCall(PetscFree(mesh->childIDs));
2555   PetscCall(PetscSectionDestroy(&mesh->childSection));
2556   PetscCall(PetscFree(mesh->children));
2557   PetscCall(DMDestroy(&mesh->referenceTree));
2558   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2559   PetscCall(PetscFree(mesh->neighbors));
2560   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2561   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2562   PetscCall(PetscFree(mesh));
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2567 {
2568   PetscSection           sectionGlobal;
2569   PetscInt               bs = -1, mbs;
2570   PetscInt               localSize, localStart = 0;
2571   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2572   MatType                mtype;
2573   ISLocalToGlobalMapping ltog;
2574 
2575   PetscFunctionBegin;
2576   PetscCall(MatInitializePackage());
2577   mtype = dm->mattype;
2578   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2579   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2580   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2581   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2582   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2583   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2584   PetscCall(MatSetType(*J, mtype));
2585   PetscCall(MatSetFromOptions(*J));
2586   PetscCall(MatGetBlockSize(*J, &mbs));
2587   if (mbs > 1) bs = mbs;
2588   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2589   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2590   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2591   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2593   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2594   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2595   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2596   if (!isShell) {
2597     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2598     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2599     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2600 
2601     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2602 
2603     PetscCall(PetscCalloc1(localSize, &pblocks));
2604     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2605     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2606     for (p = pStart; p < pEnd; ++p) {
2607       switch (dm->blocking_type) {
2608       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2609         PetscInt bdof, offset;
2610 
2611         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2612         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2613         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2614         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2615         dof  = dof < 0 ? -(dof + 1) : dof;
2616         bdof = cdof && (dof - cdof) ? 1 : dof;
2617         if (dof) {
2618           if (bs < 0) {
2619             bs = bdof;
2620           } else if (bs != bdof) {
2621             bs = 1;
2622           }
2623         }
2624       } break;
2625       case DM_BLOCKING_FIELD_NODE: {
2626         for (PetscInt field = 0; field < num_fields; field++) {
2627           PetscInt num_comp, bdof, offset;
2628           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2629           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2630           if (dof < 0) continue;
2631           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2632           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2633           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2634           PetscInt num_nodes = dof / num_comp;
2635           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2636           // Handle possibly constant block size (unlikely)
2637           bdof = cdof && (dof - cdof) ? 1 : dof;
2638           if (dof) {
2639             if (bs < 0) {
2640               bs = bdof;
2641             } else if (bs != bdof) {
2642               bs = 1;
2643             }
2644           }
2645         }
2646       } break;
2647       }
2648     }
2649     /* Must have same blocksize on all procs (some might have no points) */
2650     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2651     bsLocal[1] = bs;
2652     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2653     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2654     else bs = bsMinMax[0];
2655     bs = PetscMax(1, bs);
2656     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2657     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2658       PetscCall(MatSetBlockSize(*J, bs));
2659       PetscCall(MatSetUp(*J));
2660     } else {
2661       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2662       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2663       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2664     }
2665     { // Consolidate blocks
2666       PetscInt nblocks = 0;
2667       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2668         if (pblocks[i] == 0) continue;
2669         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2670         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]);
2671       }
2672       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2673     }
2674     PetscCall(PetscFree(pblocks));
2675   }
2676   PetscCall(MatSetDM(*J, dm));
2677   PetscFunctionReturn(PETSC_SUCCESS);
2678 }
2679 
2680 /*@
2681   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . mesh - The `DMPLEX`
2687 
2688   Output Parameter:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . mesh - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + mesh - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2754 @*/
2755 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2756 {
2757   DM_Plex *mesh = (DM_Plex *)dm->data;
2758 
2759   PetscFunctionBegin;
2760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2761   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2762   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2763   PetscCall(PetscFree(mesh->cellTypes));
2764   PetscFunctionReturn(PETSC_SUCCESS);
2765 }
2766 
2767 /*@
2768   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2769 
2770   Not Collective
2771 
2772   Input Parameters:
2773 + mesh - The `DMPLEX`
2774 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2775 
2776   Output Parameter:
2777 . size - The cone size for point `p`
2778 
2779   Level: beginner
2780 
2781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2782 @*/
2783 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2784 {
2785   DM_Plex *mesh = (DM_Plex *)dm->data;
2786 
2787   PetscFunctionBegin;
2788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2789   PetscValidIntPointer(size, 3);
2790   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2791   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2792   PetscFunctionReturn(PETSC_SUCCESS);
2793 }
2794 
2795 /*@
2796   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2797 
2798   Not Collective
2799 
2800   Input Parameters:
2801 + mesh - The `DMPLEX`
2802 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2803 - size - The cone size for point `p`
2804 
2805   Level: beginner
2806 
2807   Note:
2808   This should be called after `DMPlexSetChart()`.
2809 
2810 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2811 @*/
2812 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2813 {
2814   DM_Plex *mesh = (DM_Plex *)dm->data;
2815 
2816   PetscFunctionBegin;
2817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2818   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2819   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2820   PetscFunctionReturn(PETSC_SUCCESS);
2821 }
2822 
2823 /*@C
2824   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2825 
2826   Not Collective
2827 
2828   Input Parameters:
2829 + dm - The `DMPLEX`
2830 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2831 
2832   Output Parameter:
2833 . cone - An array of points which are on the in-edges for point `p`
2834 
2835   Level: beginner
2836 
2837   Fortran Note:
2838   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2839   `DMPlexRestoreCone()` is not needed/available in C.
2840 
2841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2842 @*/
2843 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2844 {
2845   DM_Plex *mesh = (DM_Plex *)dm->data;
2846   PetscInt off;
2847 
2848   PetscFunctionBegin;
2849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2850   PetscValidPointer(cone, 3);
2851   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2852   *cone = &mesh->cones[off];
2853   PetscFunctionReturn(PETSC_SUCCESS);
2854 }
2855 
2856 /*@C
2857   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2858 
2859   Not Collective
2860 
2861   Input Parameters:
2862 + dm - The `DMPLEX`
2863 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2864 
2865   Output Parameters:
2866 + pConesSection - `PetscSection` describing the layout of `pCones`
2867 - pCones - An array of points which are on the in-edges for the point set `p`
2868 
2869   Level: intermediate
2870 
2871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2872 @*/
2873 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2874 {
2875   PetscSection cs, newcs;
2876   PetscInt    *cones;
2877   PetscInt    *newarr = NULL;
2878   PetscInt     n;
2879 
2880   PetscFunctionBegin;
2881   PetscCall(DMPlexGetCones(dm, &cones));
2882   PetscCall(DMPlexGetConeSection(dm, &cs));
2883   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2884   if (pConesSection) *pConesSection = newcs;
2885   if (pCones) {
2886     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2887     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2888   }
2889   PetscFunctionReturn(PETSC_SUCCESS);
2890 }
2891 
2892 /*@
2893   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2894 
2895   Not Collective
2896 
2897   Input Parameters:
2898 + dm - The `DMPLEX`
2899 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2900 
2901   Output Parameter:
2902 . expandedPoints - An array of vertices recursively expanded from input points
2903 
2904   Level: advanced
2905 
2906   Notes:
2907   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2908 
2909   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2910 
2911 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2912           `DMPlexGetDepth()`, `IS`
2913 @*/
2914 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2915 {
2916   IS      *expandedPointsAll;
2917   PetscInt depth;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2922   PetscValidPointer(expandedPoints, 3);
2923   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2924   *expandedPoints = expandedPointsAll[0];
2925   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2926   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2927   PetscFunctionReturn(PETSC_SUCCESS);
2928 }
2929 
2930 /*@
2931   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).
2932 
2933   Not Collective
2934 
2935   Input Parameters:
2936 + dm - The `DMPLEX`
2937 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2938 
2939   Output Parameters:
2940 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2941 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2942 - sections - (optional) An array of sections which describe mappings from points to their cone points
2943 
2944   Level: advanced
2945 
2946   Notes:
2947   Like `DMPlexGetConeTuple()` but recursive.
2948 
2949   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.
2950   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2951 
2952   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:
2953   (1) DAG points in expandedPoints[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2954   (2) DAG points in expandedPoints[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2955 
2956 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2957           `DMPlexGetDepth()`, `PetscSection`, `IS`
2958 @*/
2959 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2960 {
2961   const PetscInt *arr0 = NULL, *cone = NULL;
2962   PetscInt       *arr = NULL, *newarr = NULL;
2963   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2964   IS             *expandedPoints_;
2965   PetscSection   *sections_;
2966 
2967   PetscFunctionBegin;
2968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2969   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2970   if (depth) PetscValidIntPointer(depth, 3);
2971   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2972   if (sections) PetscValidPointer(sections, 5);
2973   PetscCall(ISGetLocalSize(points, &n));
2974   PetscCall(ISGetIndices(points, &arr0));
2975   PetscCall(DMPlexGetDepth(dm, &depth_));
2976   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2977   PetscCall(PetscCalloc1(depth_, &sections_));
2978   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2979   for (d = depth_ - 1; d >= 0; d--) {
2980     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2981     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2982     for (i = 0; i < n; i++) {
2983       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2984       if (arr[i] >= start && arr[i] < end) {
2985         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2986         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2987       } else {
2988         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2989       }
2990     }
2991     PetscCall(PetscSectionSetUp(sections_[d]));
2992     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2993     PetscCall(PetscMalloc1(newn, &newarr));
2994     for (i = 0; i < n; i++) {
2995       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2996       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2997       if (cn > 1) {
2998         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2999         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3000       } else {
3001         newarr[co] = arr[i];
3002       }
3003     }
3004     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3005     arr = newarr;
3006     n   = newn;
3007   }
3008   PetscCall(ISRestoreIndices(points, &arr0));
3009   *depth = depth_;
3010   if (expandedPoints) *expandedPoints = expandedPoints_;
3011   else {
3012     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3013     PetscCall(PetscFree(expandedPoints_));
3014   }
3015   if (sections) *sections = sections_;
3016   else {
3017     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3018     PetscCall(PetscFree(sections_));
3019   }
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@
3024   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm - The `DMPLEX`
3030 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3031 
3032   Output Parameters:
3033 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3034 . expandedPoints - (optional) An array of recursively expanded cones
3035 - sections - (optional) An array of sections which describe mappings from points to their cone points
3036 
3037   Level: advanced
3038 
3039   Note:
3040   See `DMPlexGetConeRecursive()`
3041 
3042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3043           `DMPlexGetDepth()`, `IS`, `PetscSection`
3044 @*/
3045 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3046 {
3047   PetscInt d, depth_;
3048 
3049   PetscFunctionBegin;
3050   PetscCall(DMPlexGetDepth(dm, &depth_));
3051   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3052   if (depth) *depth = 0;
3053   if (expandedPoints) {
3054     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3055     PetscCall(PetscFree(*expandedPoints));
3056   }
3057   if (sections) {
3058     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3059     PetscCall(PetscFree(*sections));
3060   }
3061   PetscFunctionReturn(PETSC_SUCCESS);
3062 }
3063 
3064 /*@
3065   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
3066 
3067   Not Collective
3068 
3069   Input Parameters:
3070 + mesh - The `DMPLEX`
3071 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3072 - cone - An array of points which are on the in-edges for point `p`
3073 
3074   Level: beginner
3075 
3076   Note:
3077   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3078 
3079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3080 @*/
3081 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3082 {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084   PetscInt dof, off, c;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3089   if (dof) PetscValidIntPointer(cone, 3);
3090   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3091   if (PetscDefined(USE_DEBUG)) {
3092     PetscInt pStart, pEnd;
3093     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3094     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3095     for (c = 0; c < dof; ++c) {
3096       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);
3097       mesh->cones[off + c] = cone[c];
3098     }
3099   } else {
3100     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3101   }
3102   PetscFunctionReturn(PETSC_SUCCESS);
3103 }
3104 
3105 /*@C
3106   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3107 
3108   Not Collective
3109 
3110   Input Parameters:
3111 + mesh - The `DMPLEX`
3112 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3113 
3114   Output Parameter:
3115 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3116                     integer giving the prescription for cone traversal.
3117 
3118   Level: beginner
3119 
3120   Note:
3121   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3122   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3123   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3124   with the identity.
3125 
3126   Fortran Note:
3127   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3128   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3129 
3130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3131 @*/
3132 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3133 {
3134   DM_Plex *mesh = (DM_Plex *)dm->data;
3135   PetscInt off;
3136 
3137   PetscFunctionBegin;
3138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3139   if (PetscDefined(USE_DEBUG)) {
3140     PetscInt dof;
3141     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3142     if (dof) PetscValidPointer(coneOrientation, 3);
3143   }
3144   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3145 
3146   *coneOrientation = &mesh->coneOrientations[off];
3147   PetscFunctionReturn(PETSC_SUCCESS);
3148 }
3149 
3150 /*@
3151   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3152 
3153   Not Collective
3154 
3155   Input Parameters:
3156 + mesh - The `DMPLEX`
3157 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3158 - coneOrientation - An array of orientations
3159 
3160   Level: beginner
3161 
3162   Notes:
3163   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3164 
3165   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3166 
3167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3168 @*/
3169 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3170 {
3171   DM_Plex *mesh = (DM_Plex *)dm->data;
3172   PetscInt pStart, pEnd;
3173   PetscInt dof, off, c;
3174 
3175   PetscFunctionBegin;
3176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3177   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3178   if (dof) PetscValidIntPointer(coneOrientation, 3);
3179   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3180   if (PetscDefined(USE_DEBUG)) {
3181     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3182     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);
3183     for (c = 0; c < dof; ++c) {
3184       PetscInt cdof, o = coneOrientation[c];
3185 
3186       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3187       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);
3188       mesh->coneOrientations[off + c] = o;
3189     }
3190   } else {
3191     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3192   }
3193   PetscFunctionReturn(PETSC_SUCCESS);
3194 }
3195 
3196 /*@
3197   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3198 
3199   Not Collective
3200 
3201   Input Parameters:
3202 + mesh - The `DMPLEX`
3203 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3204 . conePos - The local index in the cone where the point should be put
3205 - conePoint - The mesh point to insert
3206 
3207   Level: beginner
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3210 @*/
3211 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
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   if (PetscDefined(USE_DEBUG)) {
3220     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3221     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);
3222     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);
3223     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3224     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);
3225   }
3226   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3227   mesh->cones[off + conePos] = conePoint;
3228   PetscFunctionReturn(PETSC_SUCCESS);
3229 }
3230 
3231 /*@
3232   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3233 
3234   Not Collective
3235 
3236   Input Parameters:
3237 + mesh - The `DMPLEX`
3238 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3239 . conePos - The local index in the cone where the point should be put
3240 - coneOrientation - The point orientation to insert
3241 
3242   Level: beginner
3243 
3244   Note:
3245   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3246 
3247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3248 @*/
3249 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3250 {
3251   DM_Plex *mesh = (DM_Plex *)dm->data;
3252   PetscInt pStart, pEnd;
3253   PetscInt dof, off;
3254 
3255   PetscFunctionBegin;
3256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3257   if (PetscDefined(USE_DEBUG)) {
3258     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3259     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);
3260     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3261     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);
3262   }
3263   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3264   mesh->coneOrientations[off + conePos] = coneOrientation;
3265   PetscFunctionReturn(PETSC_SUCCESS);
3266 }
3267 
3268 /*@C
3269   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3270 
3271   Not collective
3272 
3273   Input Parameters:
3274 + dm - The DMPlex
3275 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3276 
3277   Output Parameters:
3278 + cone - An array of points which are on the in-edges for point `p`
3279 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3280         integer giving the prescription for cone traversal.
3281 
3282   Level: beginner
3283 
3284   Notes:
3285   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3286   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3287   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3288   with the identity.
3289 
3290   Fortran Notes:
3291   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3292   `DMPlexRestoreCone()` is not needed/available in C.
3293 
3294 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3295 @*/
3296 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3297 {
3298   DM_Plex *mesh = (DM_Plex *)dm->data;
3299 
3300   PetscFunctionBegin;
3301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3302   if (mesh->tr) {
3303     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3304   } else {
3305     PetscInt off;
3306     if (PetscDefined(USE_DEBUG)) {
3307       PetscInt dof;
3308       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3309       if (dof) {
3310         if (cone) PetscValidPointer(cone, 3);
3311         if (ornt) PetscValidPointer(ornt, 4);
3312       }
3313     }
3314     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3315     if (cone) *cone = &mesh->cones[off];
3316     if (ornt) *ornt = &mesh->coneOrientations[off];
3317   }
3318   PetscFunctionReturn(PETSC_SUCCESS);
3319 }
3320 
3321 /*@C
3322   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3323 
3324   Not Collective
3325 
3326   Input Parameters:
3327 + dm - The DMPlex
3328 . p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3329 . cone - An array of points which are on the in-edges for point p
3330 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3331         integer giving the prescription for cone traversal.
3332 
3333   Level: beginner
3334 
3335   Notes:
3336   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3337   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3338   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3339   with the identity.
3340 
3341   Fortran Note:
3342   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3343   `DMPlexRestoreCone()` is not needed/available in C.
3344 
3345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3346 @*/
3347 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3348 {
3349   DM_Plex *mesh = (DM_Plex *)dm->data;
3350 
3351   PetscFunctionBegin;
3352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3353   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3354   PetscFunctionReturn(PETSC_SUCCESS);
3355 }
3356 
3357 /*@
3358   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + mesh - The `DMPLEX`
3364 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 
3366   Output Parameter:
3367 . size - The support size for point `p`
3368 
3369   Level: beginner
3370 
3371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3372 @*/
3373 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3374 {
3375   DM_Plex *mesh = (DM_Plex *)dm->data;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   PetscValidIntPointer(size, 3);
3380   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(PETSC_SUCCESS);
3382 }
3383 
3384 /*@
3385   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + mesh - The `DMPLEX`
3391 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 - size - The support size for point `p`
3393 
3394   Level: beginner
3395 
3396   Note:
3397   This should be called after `DMPlexSetChart()`.
3398 
3399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3400 @*/
3401 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3402 {
3403   DM_Plex *mesh = (DM_Plex *)dm->data;
3404 
3405   PetscFunctionBegin;
3406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3407   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3408   PetscFunctionReturn(PETSC_SUCCESS);
3409 }
3410 
3411 /*@C
3412   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3413 
3414   Not Collective
3415 
3416   Input Parameters:
3417 + mesh - The `DMPLEX`
3418 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3419 
3420   Output Parameter:
3421 . support - An array of points which are on the out-edges for point `p`
3422 
3423   Level: beginner
3424 
3425   Fortran Note:
3426   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3427   `DMPlexRestoreSupport()` is not needed/available in C.
3428 
3429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3430 @*/
3431 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3432 {
3433   DM_Plex *mesh = (DM_Plex *)dm->data;
3434   PetscInt off;
3435 
3436   PetscFunctionBegin;
3437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3438   PetscValidPointer(support, 3);
3439   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3440   *support = &mesh->supports[off];
3441   PetscFunctionReturn(PETSC_SUCCESS);
3442 }
3443 
3444 /*@
3445   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3446 
3447   Not Collective
3448 
3449   Input Parameters:
3450 + mesh - The `DMPLEX`
3451 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3452 - support - An array of points which are on the out-edges for point `p`
3453 
3454   Level: beginner
3455 
3456   Note:
3457   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3458 
3459 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3460 @*/
3461 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3462 {
3463   DM_Plex *mesh = (DM_Plex *)dm->data;
3464   PetscInt pStart, pEnd;
3465   PetscInt dof, off, c;
3466 
3467   PetscFunctionBegin;
3468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3469   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3470   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3471   if (dof) PetscValidIntPointer(support, 3);
3472   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3473   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);
3474   for (c = 0; c < dof; ++c) {
3475     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);
3476     mesh->supports[off + c] = support[c];
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@
3482   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + mesh - The `DMPLEX`
3488 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . supportPos - The local index in the cone where the point should be put
3490 - supportPoint - The mesh point to insert
3491 
3492   Level: beginner
3493 
3494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3495 @*/
3496 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3497 {
3498   DM_Plex *mesh = (DM_Plex *)dm->data;
3499   PetscInt pStart, pEnd;
3500   PetscInt dof, off;
3501 
3502   PetscFunctionBegin;
3503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3504   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3505   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3506   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3507   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);
3508   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);
3509   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);
3510   mesh->supports[off + supportPos] = supportPoint;
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3515 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3516 {
3517   switch (ct) {
3518   case DM_POLYTOPE_SEGMENT:
3519     if (o == -1) return -2;
3520     break;
3521   case DM_POLYTOPE_TRIANGLE:
3522     if (o == -3) return -1;
3523     if (o == -2) return -3;
3524     if (o == -1) return -2;
3525     break;
3526   case DM_POLYTOPE_QUADRILATERAL:
3527     if (o == -4) return -2;
3528     if (o == -3) return -1;
3529     if (o == -2) return -4;
3530     if (o == -1) return -3;
3531     break;
3532   default:
3533     return o;
3534   }
3535   return o;
3536 }
3537 
3538 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3539 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3540 {
3541   switch (ct) {
3542   case DM_POLYTOPE_SEGMENT:
3543     if ((o == -2) || (o == 1)) return -1;
3544     if (o == -1) return 0;
3545     break;
3546   case DM_POLYTOPE_TRIANGLE:
3547     if (o == -3) return -2;
3548     if (o == -2) return -1;
3549     if (o == -1) return -3;
3550     break;
3551   case DM_POLYTOPE_QUADRILATERAL:
3552     if (o == -4) return -2;
3553     if (o == -3) return -1;
3554     if (o == -2) return -4;
3555     if (o == -1) return -3;
3556     break;
3557   default:
3558     return o;
3559   }
3560   return o;
3561 }
3562 
3563 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3564 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3565 {
3566   PetscInt pStart, pEnd, p;
3567 
3568   PetscFunctionBegin;
3569   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3570   for (p = pStart; p < pEnd; ++p) {
3571     const PetscInt *cone, *ornt;
3572     PetscInt        coneSize, c;
3573 
3574     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3575     PetscCall(DMPlexGetCone(dm, p, &cone));
3576     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3577     for (c = 0; c < coneSize; ++c) {
3578       DMPolytopeType ct;
3579       const PetscInt o = ornt[c];
3580 
3581       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3582       switch (ct) {
3583       case DM_POLYTOPE_SEGMENT:
3584         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3585         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3586         break;
3587       case DM_POLYTOPE_TRIANGLE:
3588         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3589         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3590         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3591         break;
3592       case DM_POLYTOPE_QUADRILATERAL:
3593         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3594         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3595         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3596         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3597         break;
3598       default:
3599         break;
3600       }
3601     }
3602   }
3603   PetscFunctionReturn(PETSC_SUCCESS);
3604 }
3605 
3606 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3607 {
3608   DM_Plex *mesh = (DM_Plex *)dm->data;
3609 
3610   PetscFunctionBeginHot;
3611   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3612     if (useCone) {
3613       PetscCall(DMPlexGetConeSize(dm, p, size));
3614       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3615     } else {
3616       PetscCall(DMPlexGetSupportSize(dm, p, size));
3617       PetscCall(DMPlexGetSupport(dm, p, arr));
3618     }
3619   } else {
3620     if (useCone) {
3621       const PetscSection s   = mesh->coneSection;
3622       const PetscInt     ps  = p - s->pStart;
3623       const PetscInt     off = s->atlasOff[ps];
3624 
3625       *size = s->atlasDof[ps];
3626       *arr  = mesh->cones + off;
3627       *ornt = mesh->coneOrientations + off;
3628     } else {
3629       const PetscSection s   = mesh->supportSection;
3630       const PetscInt     ps  = p - s->pStart;
3631       const PetscInt     off = s->atlasOff[ps];
3632 
3633       *size = s->atlasDof[ps];
3634       *arr  = mesh->supports + off;
3635     }
3636   }
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3641 {
3642   DM_Plex *mesh = (DM_Plex *)dm->data;
3643 
3644   PetscFunctionBeginHot;
3645   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3646     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3647   }
3648   PetscFunctionReturn(PETSC_SUCCESS);
3649 }
3650 
3651 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3652 {
3653   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3654   PetscInt       *closure;
3655   const PetscInt *tmp = NULL, *tmpO = NULL;
3656   PetscInt        off = 0, tmpSize, t;
3657 
3658   PetscFunctionBeginHot;
3659   if (ornt) {
3660     PetscCall(DMPlexGetCellType(dm, p, &ct));
3661     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3662   }
3663   if (*points) {
3664     closure = *points;
3665   } else {
3666     PetscInt maxConeSize, maxSupportSize;
3667     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3668     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3669   }
3670   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3671   if (ct == DM_POLYTOPE_UNKNOWN) {
3672     closure[off++] = p;
3673     closure[off++] = 0;
3674     for (t = 0; t < tmpSize; ++t) {
3675       closure[off++] = tmp[t];
3676       closure[off++] = tmpO ? tmpO[t] : 0;
3677     }
3678   } else {
3679     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3680 
3681     /* We assume that cells with a valid type have faces with a valid type */
3682     closure[off++] = p;
3683     closure[off++] = ornt;
3684     for (t = 0; t < tmpSize; ++t) {
3685       DMPolytopeType ft;
3686 
3687       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3688       closure[off++] = tmp[arr[t]];
3689       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3690     }
3691   }
3692   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3693   if (numPoints) *numPoints = tmpSize + 1;
3694   if (points) *points = closure;
3695   PetscFunctionReturn(PETSC_SUCCESS);
3696 }
3697 
3698 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3699 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3700 {
3701   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3702   const PetscInt *cone, *ornt;
3703   PetscInt       *pts, *closure = NULL;
3704   DMPolytopeType  ft;
3705   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3706   PetscInt        dim, coneSize, c, d, clSize, cl;
3707 
3708   PetscFunctionBeginHot;
3709   PetscCall(DMGetDimension(dm, &dim));
3710   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3711   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3712   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3713   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3714   maxSize       = PetscMax(coneSeries, supportSeries);
3715   if (*points) {
3716     pts = *points;
3717   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3718   c        = 0;
3719   pts[c++] = point;
3720   pts[c++] = o;
3721   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3722   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3723   for (cl = 0; cl < clSize * 2; cl += 2) {
3724     pts[c++] = closure[cl];
3725     pts[c++] = closure[cl + 1];
3726   }
3727   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3728   for (cl = 0; cl < clSize * 2; cl += 2) {
3729     pts[c++] = closure[cl];
3730     pts[c++] = closure[cl + 1];
3731   }
3732   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3733   for (d = 2; d < coneSize; ++d) {
3734     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3735     pts[c++] = cone[arr[d * 2 + 0]];
3736     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3737   }
3738   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3739   if (dim >= 3) {
3740     for (d = 2; d < coneSize; ++d) {
3741       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3742       const PetscInt *fcone, *fornt;
3743       PetscInt        fconeSize, fc, i;
3744 
3745       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3746       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3747       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3748       for (fc = 0; fc < fconeSize; ++fc) {
3749         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3750         const PetscInt co = farr[fc * 2 + 1];
3751 
3752         for (i = 0; i < c; i += 2)
3753           if (pts[i] == cp) break;
3754         if (i == c) {
3755           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3756           pts[c++] = cp;
3757           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3758         }
3759       }
3760       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3761     }
3762   }
3763   *numPoints = c / 2;
3764   *points    = pts;
3765   PetscFunctionReturn(PETSC_SUCCESS);
3766 }
3767 
3768 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3769 {
3770   DMPolytopeType ct;
3771   PetscInt      *closure, *fifo;
3772   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3773   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3774   PetscInt       depth, maxSize;
3775 
3776   PetscFunctionBeginHot;
3777   PetscCall(DMPlexGetDepth(dm, &depth));
3778   if (depth == 1) {
3779     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3780     PetscFunctionReturn(PETSC_SUCCESS);
3781   }
3782   PetscCall(DMPlexGetCellType(dm, p, &ct));
3783   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3784   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3785     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3786     PetscFunctionReturn(PETSC_SUCCESS);
3787   }
3788   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3789   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3790   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3791   maxSize       = PetscMax(coneSeries, supportSeries);
3792   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3793   if (*points) {
3794     closure = *points;
3795   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3796   closure[closureSize++] = p;
3797   closure[closureSize++] = ornt;
3798   fifo[fifoSize++]       = p;
3799   fifo[fifoSize++]       = ornt;
3800   fifo[fifoSize++]       = ct;
3801   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3802   while (fifoSize - fifoStart) {
3803     const PetscInt       q    = fifo[fifoStart++];
3804     const PetscInt       o    = fifo[fifoStart++];
3805     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3806     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3807     const PetscInt      *tmp, *tmpO = NULL;
3808     PetscInt             tmpSize, t;
3809 
3810     if (PetscDefined(USE_DEBUG)) {
3811       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3812       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);
3813     }
3814     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3815     for (t = 0; t < tmpSize; ++t) {
3816       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3817       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3818       const PetscInt cp = tmp[ip];
3819       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3820       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3821       PetscInt       c;
3822 
3823       /* Check for duplicate */
3824       for (c = 0; c < closureSize; c += 2) {
3825         if (closure[c] == cp) break;
3826       }
3827       if (c == closureSize) {
3828         closure[closureSize++] = cp;
3829         closure[closureSize++] = co;
3830         fifo[fifoSize++]       = cp;
3831         fifo[fifoSize++]       = co;
3832         fifo[fifoSize++]       = ct;
3833       }
3834     }
3835     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3836   }
3837   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3838   if (numPoints) *numPoints = closureSize / 2;
3839   if (points) *points = closure;
3840   PetscFunctionReturn(PETSC_SUCCESS);
3841 }
3842 
3843 /*@C
3844   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3845 
3846   Not Collective
3847 
3848   Input Parameters:
3849 + dm      - The `DMPLEX`
3850 . p       - The mesh point
3851 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3852 
3853   Input/Output Parameter:
3854 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3855            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3856 
3857   Output Parameter:
3858 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3859 
3860   Level: beginner
3861 
3862   Note:
3863   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3864 
3865   Fortran Note:
3866   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3867 
3868 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3869 @*/
3870 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3871 {
3872   PetscFunctionBeginHot;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   if (numPoints) PetscValidIntPointer(numPoints, 4);
3875   if (points) PetscValidPointer(points, 5);
3876   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3877   PetscFunctionReturn(PETSC_SUCCESS);
3878 }
3879 
3880 /*@C
3881   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3882 
3883   Not Collective
3884 
3885   Input Parameters:
3886 + dm        - The `DMPLEX`
3887 . p         - The mesh point
3888 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3889 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3890 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3891 
3892   Level: beginner
3893 
3894   Note:
3895   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3896 
3897 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3898 @*/
3899 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3900 {
3901   PetscFunctionBeginHot;
3902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3903   if (numPoints) *numPoints = 0;
3904   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3905   PetscFunctionReturn(PETSC_SUCCESS);
3906 }
3907 
3908 /*@
3909   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3910 
3911   Not Collective
3912 
3913   Input Parameter:
3914 . mesh - The `DMPLEX`
3915 
3916   Output Parameters:
3917 + maxConeSize - The maximum number of in-edges
3918 - maxSupportSize - The maximum number of out-edges
3919 
3920   Level: beginner
3921 
3922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3923 @*/
3924 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3925 {
3926   DM_Plex *mesh = (DM_Plex *)dm->data;
3927 
3928   PetscFunctionBegin;
3929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3930   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3931   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3932   PetscFunctionReturn(PETSC_SUCCESS);
3933 }
3934 
3935 PetscErrorCode DMSetUp_Plex(DM dm)
3936 {
3937   DM_Plex *mesh = (DM_Plex *)dm->data;
3938   PetscInt size, maxSupportSize;
3939 
3940   PetscFunctionBegin;
3941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3942   PetscCall(PetscSectionSetUp(mesh->coneSection));
3943   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3944   PetscCall(PetscMalloc1(size, &mesh->cones));
3945   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3946   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3947   if (maxSupportSize) {
3948     PetscCall(PetscSectionSetUp(mesh->supportSection));
3949     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3950     PetscCall(PetscMalloc1(size, &mesh->supports));
3951   }
3952   PetscFunctionReturn(PETSC_SUCCESS);
3953 }
3954 
3955 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3956 {
3957   PetscFunctionBegin;
3958   if (subdm) PetscCall(DMClone(dm, subdm));
3959   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3960   if (subdm) (*subdm)->useNatural = dm->useNatural;
3961   if (dm->useNatural && dm->sfMigration) {
3962     PetscSF sfNatural;
3963 
3964     (*subdm)->sfMigration = dm->sfMigration;
3965     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3966     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3967     (*subdm)->sfNatural = sfNatural;
3968   }
3969   PetscFunctionReturn(PETSC_SUCCESS);
3970 }
3971 
3972 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3973 {
3974   PetscInt i = 0;
3975 
3976   PetscFunctionBegin;
3977   PetscCall(DMClone(dms[0], superdm));
3978   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3979   (*superdm)->useNatural = PETSC_FALSE;
3980   for (i = 0; i < len; i++) {
3981     if (dms[i]->useNatural && dms[i]->sfMigration) {
3982       PetscSF sfNatural;
3983 
3984       (*superdm)->sfMigration = dms[i]->sfMigration;
3985       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3986       (*superdm)->useNatural = PETSC_TRUE;
3987       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3988       (*superdm)->sfNatural = sfNatural;
3989       break;
3990     }
3991   }
3992   PetscFunctionReturn(PETSC_SUCCESS);
3993 }
3994 
3995 /*@
3996   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3997 
3998   Not Collective
3999 
4000   Input Parameter:
4001 . mesh - The `DMPLEX`
4002 
4003   Level: beginner
4004 
4005   Note:
4006   This should be called after all calls to `DMPlexSetCone()`
4007 
4008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4009 @*/
4010 PetscErrorCode DMPlexSymmetrize(DM dm)
4011 {
4012   DM_Plex  *mesh = (DM_Plex *)dm->data;
4013   PetscInt *offsets;
4014   PetscInt  supportSize;
4015   PetscInt  pStart, pEnd, p;
4016 
4017   PetscFunctionBegin;
4018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4019   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4020   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4021   /* Calculate support sizes */
4022   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4023   for (p = pStart; p < pEnd; ++p) {
4024     PetscInt dof, off, c;
4025 
4026     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4027     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4028     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4029   }
4030   PetscCall(PetscSectionSetUp(mesh->supportSection));
4031   /* Calculate supports */
4032   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4033   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4034   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4035   for (p = pStart; p < pEnd; ++p) {
4036     PetscInt dof, off, c;
4037 
4038     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4039     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4040     for (c = off; c < off + dof; ++c) {
4041       const PetscInt q = mesh->cones[c];
4042       PetscInt       offS;
4043 
4044       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4045 
4046       mesh->supports[offS + offsets[q]] = p;
4047       ++offsets[q];
4048     }
4049   }
4050   PetscCall(PetscFree(offsets));
4051   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4052   PetscFunctionReturn(PETSC_SUCCESS);
4053 }
4054 
4055 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4056 {
4057   IS stratumIS;
4058 
4059   PetscFunctionBegin;
4060   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4061   if (PetscDefined(USE_DEBUG)) {
4062     PetscInt  qStart, qEnd, numLevels, level;
4063     PetscBool overlap = PETSC_FALSE;
4064     PetscCall(DMLabelGetNumValues(label, &numLevels));
4065     for (level = 0; level < numLevels; level++) {
4066       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4067       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4068         overlap = PETSC_TRUE;
4069         break;
4070       }
4071     }
4072     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);
4073   }
4074   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4075   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4076   PetscCall(ISDestroy(&stratumIS));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@
4081   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4082   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4083   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4084   the DAG.
4085 
4086   Collective
4087 
4088   Input Parameter:
4089 . mesh - The `DMPLEX`
4090 
4091   Level: beginner
4092 
4093   Notes:
4094   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4095   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4096   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4097   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4098   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4099 
4100   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4101   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4102   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
4103   to interpolate only that one (e0), so that
4104 .vb
4105   cone(c0) = {e0, v2}
4106   cone(e0) = {v0, v1}
4107 .ve
4108   If `DMPlexStratify()` is run on this mesh, it will give depths
4109 .vb
4110    depth 0 = {v0, v1, v2}
4111    depth 1 = {e0, c0}
4112 .ve
4113   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4114 
4115   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4116 
4117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4118 @*/
4119 PetscErrorCode DMPlexStratify(DM dm)
4120 {
4121   DM_Plex *mesh = (DM_Plex *)dm->data;
4122   DMLabel  label;
4123   PetscInt pStart, pEnd, p;
4124   PetscInt numRoots = 0, numLeaves = 0;
4125 
4126   PetscFunctionBegin;
4127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4128   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4129 
4130   /* Create depth label */
4131   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4132   PetscCall(DMCreateLabel(dm, "depth"));
4133   PetscCall(DMPlexGetDepthLabel(dm, &label));
4134 
4135   {
4136     /* Initialize roots and count leaves */
4137     PetscInt sMin = PETSC_MAX_INT;
4138     PetscInt sMax = PETSC_MIN_INT;
4139     PetscInt coneSize, supportSize;
4140 
4141     for (p = pStart; p < pEnd; ++p) {
4142       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4143       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4144       if (!coneSize && supportSize) {
4145         sMin = PetscMin(p, sMin);
4146         sMax = PetscMax(p, sMax);
4147         ++numRoots;
4148       } else if (!supportSize && coneSize) {
4149         ++numLeaves;
4150       } else if (!supportSize && !coneSize) {
4151         /* Isolated points */
4152         sMin = PetscMin(p, sMin);
4153         sMax = PetscMax(p, sMax);
4154       }
4155     }
4156     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4157   }
4158 
4159   if (numRoots + numLeaves == (pEnd - pStart)) {
4160     PetscInt sMin = PETSC_MAX_INT;
4161     PetscInt sMax = PETSC_MIN_INT;
4162     PetscInt coneSize, supportSize;
4163 
4164     for (p = pStart; p < pEnd; ++p) {
4165       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4166       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4167       if (!supportSize && coneSize) {
4168         sMin = PetscMin(p, sMin);
4169         sMax = PetscMax(p, sMax);
4170       }
4171     }
4172     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4173   } else {
4174     PetscInt level = 0;
4175     PetscInt qStart, qEnd, q;
4176 
4177     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4178     while (qEnd > qStart) {
4179       PetscInt sMin = PETSC_MAX_INT;
4180       PetscInt sMax = PETSC_MIN_INT;
4181 
4182       for (q = qStart; q < qEnd; ++q) {
4183         const PetscInt *support;
4184         PetscInt        supportSize, s;
4185 
4186         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4187         PetscCall(DMPlexGetSupport(dm, q, &support));
4188         for (s = 0; s < supportSize; ++s) {
4189           sMin = PetscMin(support[s], sMin);
4190           sMax = PetscMax(support[s], sMax);
4191         }
4192       }
4193       PetscCall(DMLabelGetNumValues(label, &level));
4194       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4195       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4196     }
4197   }
4198   { /* just in case there is an empty process */
4199     PetscInt numValues, maxValues = 0, v;
4200 
4201     PetscCall(DMLabelGetNumValues(label, &numValues));
4202     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4203     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4204   }
4205   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4206   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4207   PetscFunctionReturn(PETSC_SUCCESS);
4208 }
4209 
4210 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4211 {
4212   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4213   PetscInt       dim, depth, pheight, coneSize;
4214 
4215   PetscFunctionBeginHot;
4216   PetscCall(DMGetDimension(dm, &dim));
4217   PetscCall(DMPlexGetDepth(dm, &depth));
4218   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4219   pheight = depth - pdepth;
4220   if (depth <= 1) {
4221     switch (pdepth) {
4222     case 0:
4223       ct = DM_POLYTOPE_POINT;
4224       break;
4225     case 1:
4226       switch (coneSize) {
4227       case 2:
4228         ct = DM_POLYTOPE_SEGMENT;
4229         break;
4230       case 3:
4231         ct = DM_POLYTOPE_TRIANGLE;
4232         break;
4233       case 4:
4234         switch (dim) {
4235         case 2:
4236           ct = DM_POLYTOPE_QUADRILATERAL;
4237           break;
4238         case 3:
4239           ct = DM_POLYTOPE_TETRAHEDRON;
4240           break;
4241         default:
4242           break;
4243         }
4244         break;
4245       case 5:
4246         ct = DM_POLYTOPE_PYRAMID;
4247         break;
4248       case 6:
4249         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4250         break;
4251       case 8:
4252         ct = DM_POLYTOPE_HEXAHEDRON;
4253         break;
4254       default:
4255         break;
4256       }
4257     }
4258   } else {
4259     if (pdepth == 0) {
4260       ct = DM_POLYTOPE_POINT;
4261     } else if (pheight == 0) {
4262       switch (dim) {
4263       case 1:
4264         switch (coneSize) {
4265         case 2:
4266           ct = DM_POLYTOPE_SEGMENT;
4267           break;
4268         default:
4269           break;
4270         }
4271         break;
4272       case 2:
4273         switch (coneSize) {
4274         case 3:
4275           ct = DM_POLYTOPE_TRIANGLE;
4276           break;
4277         case 4:
4278           ct = DM_POLYTOPE_QUADRILATERAL;
4279           break;
4280         default:
4281           break;
4282         }
4283         break;
4284       case 3:
4285         switch (coneSize) {
4286         case 4:
4287           ct = DM_POLYTOPE_TETRAHEDRON;
4288           break;
4289         case 5: {
4290           const PetscInt *cone;
4291           PetscInt        faceConeSize;
4292 
4293           PetscCall(DMPlexGetCone(dm, p, &cone));
4294           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4295           switch (faceConeSize) {
4296           case 3:
4297             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4298             break;
4299           case 4:
4300             ct = DM_POLYTOPE_PYRAMID;
4301             break;
4302           }
4303         } break;
4304         case 6:
4305           ct = DM_POLYTOPE_HEXAHEDRON;
4306           break;
4307         default:
4308           break;
4309         }
4310         break;
4311       default:
4312         break;
4313       }
4314     } else if (pheight > 0) {
4315       switch (coneSize) {
4316       case 2:
4317         ct = DM_POLYTOPE_SEGMENT;
4318         break;
4319       case 3:
4320         ct = DM_POLYTOPE_TRIANGLE;
4321         break;
4322       case 4:
4323         ct = DM_POLYTOPE_QUADRILATERAL;
4324         break;
4325       default:
4326         break;
4327       }
4328     }
4329   }
4330   *pt = ct;
4331   PetscFunctionReturn(PETSC_SUCCESS);
4332 }
4333 
4334 /*@
4335   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4336 
4337   Collective
4338 
4339   Input Parameter:
4340 . mesh - The `DMPLEX`
4341 
4342   Level: developer
4343 
4344   Note:
4345   This function is normally called automatically when a cell type is requested. It creates an
4346   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4347   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4348 
4349   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4350 
4351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4352 @*/
4353 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4354 {
4355   DM_Plex *mesh;
4356   DMLabel  ctLabel;
4357   PetscInt pStart, pEnd, p;
4358 
4359   PetscFunctionBegin;
4360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4361   mesh = (DM_Plex *)dm->data;
4362   PetscCall(DMCreateLabel(dm, "celltype"));
4363   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4364   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4365   PetscCall(PetscFree(mesh->cellTypes));
4366   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4367   for (p = pStart; p < pEnd; ++p) {
4368     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4369     PetscInt       pdepth;
4370 
4371     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4372     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4373     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4374     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4375     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4376   }
4377   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4378   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4379   PetscFunctionReturn(PETSC_SUCCESS);
4380 }
4381 
4382 /*@C
4383   DMPlexGetJoin - Get an array for the join of the set of points
4384 
4385   Not Collective
4386 
4387   Input Parameters:
4388 + dm - The `DMPLEX` object
4389 . numPoints - The number of input points for the join
4390 - points - The input points
4391 
4392   Output Parameters:
4393 + numCoveredPoints - The number of points in the join
4394 - coveredPoints - The points in the join
4395 
4396   Level: intermediate
4397 
4398   Note:
4399   Currently, this is restricted to a single level join
4400 
4401   Fortran Note:
4402   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4403 
4404 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4405 @*/
4406 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4407 {
4408   DM_Plex  *mesh = (DM_Plex *)dm->data;
4409   PetscInt *join[2];
4410   PetscInt  joinSize, i = 0;
4411   PetscInt  dof, off, p, c, m;
4412   PetscInt  maxSupportSize;
4413 
4414   PetscFunctionBegin;
4415   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4416   PetscValidIntPointer(points, 3);
4417   PetscValidIntPointer(numCoveredPoints, 4);
4418   PetscValidPointer(coveredPoints, 5);
4419   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4420   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4421   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4422   /* Copy in support of first point */
4423   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4424   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4425   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4426   /* Check each successive support */
4427   for (p = 1; p < numPoints; ++p) {
4428     PetscInt newJoinSize = 0;
4429 
4430     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4431     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4432     for (c = 0; c < dof; ++c) {
4433       const PetscInt point = mesh->supports[off + c];
4434 
4435       for (m = 0; m < joinSize; ++m) {
4436         if (point == join[i][m]) {
4437           join[1 - i][newJoinSize++] = point;
4438           break;
4439         }
4440       }
4441     }
4442     joinSize = newJoinSize;
4443     i        = 1 - i;
4444   }
4445   *numCoveredPoints = joinSize;
4446   *coveredPoints    = join[i];
4447   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4448   PetscFunctionReturn(PETSC_SUCCESS);
4449 }
4450 
4451 /*@C
4452   DMPlexRestoreJoin - Restore an array for the join of the set of points
4453 
4454   Not Collective
4455 
4456   Input Parameters:
4457 + dm - The `DMPLEX` object
4458 . numPoints - The number of input points for the join
4459 - points - The input points
4460 
4461   Output Parameters:
4462 + numCoveredPoints - The number of points in the join
4463 - coveredPoints - The points in the join
4464 
4465   Level: intermediate
4466 
4467   Fortran Note:
4468   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4469 
4470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4471 @*/
4472 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4473 {
4474   PetscFunctionBegin;
4475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4476   if (points) PetscValidIntPointer(points, 3);
4477   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4478   PetscValidPointer(coveredPoints, 5);
4479   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4480   if (numCoveredPoints) *numCoveredPoints = 0;
4481   PetscFunctionReturn(PETSC_SUCCESS);
4482 }
4483 
4484 /*@C
4485   DMPlexGetFullJoin - Get an array for the join of the set of points
4486 
4487   Not Collective
4488 
4489   Input Parameters:
4490 + dm - The `DMPLEX` object
4491 . numPoints - The number of input points for the join
4492 - points - The input points
4493 
4494   Output Parameters:
4495 + numCoveredPoints - The number of points in the join
4496 - coveredPoints - The points in the join
4497 
4498   Level: intermediate
4499 
4500   Fortran Note:
4501   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4502 
4503 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4504 @*/
4505 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4506 {
4507   PetscInt *offsets, **closures;
4508   PetscInt *join[2];
4509   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4510   PetscInt  p, d, c, m, ms;
4511 
4512   PetscFunctionBegin;
4513   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4514   PetscValidIntPointer(points, 3);
4515   PetscValidIntPointer(numCoveredPoints, 4);
4516   PetscValidPointer(coveredPoints, 5);
4517 
4518   PetscCall(DMPlexGetDepth(dm, &depth));
4519   PetscCall(PetscCalloc1(numPoints, &closures));
4520   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4521   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4522   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4523   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4524   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4525 
4526   for (p = 0; p < numPoints; ++p) {
4527     PetscInt closureSize;
4528 
4529     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4530 
4531     offsets[p * (depth + 2) + 0] = 0;
4532     for (d = 0; d < depth + 1; ++d) {
4533       PetscInt pStart, pEnd, i;
4534 
4535       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4536       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4537         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4538           offsets[p * (depth + 2) + d + 1] = i;
4539           break;
4540         }
4541       }
4542       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4543     }
4544     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);
4545   }
4546   for (d = 0; d < depth + 1; ++d) {
4547     PetscInt dof;
4548 
4549     /* Copy in support of first point */
4550     dof = offsets[d + 1] - offsets[d];
4551     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4552     /* Check each successive cone */
4553     for (p = 1; p < numPoints && joinSize; ++p) {
4554       PetscInt newJoinSize = 0;
4555 
4556       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4557       for (c = 0; c < dof; ++c) {
4558         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4559 
4560         for (m = 0; m < joinSize; ++m) {
4561           if (point == join[i][m]) {
4562             join[1 - i][newJoinSize++] = point;
4563             break;
4564           }
4565         }
4566       }
4567       joinSize = newJoinSize;
4568       i        = 1 - i;
4569     }
4570     if (joinSize) break;
4571   }
4572   *numCoveredPoints = joinSize;
4573   *coveredPoints    = join[i];
4574   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4575   PetscCall(PetscFree(closures));
4576   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4577   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4578   PetscFunctionReturn(PETSC_SUCCESS);
4579 }
4580 
4581 /*@C
4582   DMPlexGetMeet - Get an array for the meet of the set of points
4583 
4584   Not Collective
4585 
4586   Input Parameters:
4587 + dm - The `DMPLEX` object
4588 . numPoints - The number of input points for the meet
4589 - points - The input points
4590 
4591   Output Parameters:
4592 + numCoveredPoints - The number of points in the meet
4593 - coveredPoints - The points in the meet
4594 
4595   Level: intermediate
4596 
4597   Note:
4598   Currently, this is restricted to a single level meet
4599 
4600   Fortran Notes:
4601   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4602 
4603 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4604 @*/
4605 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4606 {
4607   DM_Plex  *mesh = (DM_Plex *)dm->data;
4608   PetscInt *meet[2];
4609   PetscInt  meetSize, i = 0;
4610   PetscInt  dof, off, p, c, m;
4611   PetscInt  maxConeSize;
4612 
4613   PetscFunctionBegin;
4614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4615   PetscValidIntPointer(points, 3);
4616   PetscValidIntPointer(numCoveringPoints, 4);
4617   PetscValidPointer(coveringPoints, 5);
4618   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4619   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4620   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4621   /* Copy in cone of first point */
4622   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4623   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4624   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4625   /* Check each successive cone */
4626   for (p = 1; p < numPoints; ++p) {
4627     PetscInt newMeetSize = 0;
4628 
4629     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4630     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4631     for (c = 0; c < dof; ++c) {
4632       const PetscInt point = mesh->cones[off + c];
4633 
4634       for (m = 0; m < meetSize; ++m) {
4635         if (point == meet[i][m]) {
4636           meet[1 - i][newMeetSize++] = point;
4637           break;
4638         }
4639       }
4640     }
4641     meetSize = newMeetSize;
4642     i        = 1 - i;
4643   }
4644   *numCoveringPoints = meetSize;
4645   *coveringPoints    = meet[i];
4646   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4647   PetscFunctionReturn(PETSC_SUCCESS);
4648 }
4649 
4650 /*@C
4651   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4652 
4653   Not Collective
4654 
4655   Input Parameters:
4656 + dm - The `DMPLEX` object
4657 . numPoints - The number of input points for the meet
4658 - points - The input points
4659 
4660   Output Parameters:
4661 + numCoveredPoints - The number of points in the meet
4662 - coveredPoints - The points in the meet
4663 
4664   Level: intermediate
4665 
4666   Fortran Note:
4667   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4668 
4669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4670 @*/
4671 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4672 {
4673   PetscFunctionBegin;
4674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4675   if (points) PetscValidIntPointer(points, 3);
4676   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4677   PetscValidPointer(coveredPoints, 5);
4678   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4679   if (numCoveredPoints) *numCoveredPoints = 0;
4680   PetscFunctionReturn(PETSC_SUCCESS);
4681 }
4682 
4683 /*@C
4684   DMPlexGetFullMeet - Get an array for the meet of the set of points
4685 
4686   Not Collective
4687 
4688   Input Parameters:
4689 + dm - The `DMPLEX` object
4690 . numPoints - The number of input points for the meet
4691 - points - The input points
4692 
4693   Output Parameters:
4694 + numCoveredPoints - The number of points in the meet
4695 - coveredPoints - The points in the meet
4696 
4697   Level: intermediate
4698 
4699   Fortran Note:
4700   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4701 
4702 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4703 @*/
4704 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4705 {
4706   PetscInt *offsets, **closures;
4707   PetscInt *meet[2];
4708   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4709   PetscInt  p, h, c, m, mc;
4710 
4711   PetscFunctionBegin;
4712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4713   PetscValidIntPointer(points, 3);
4714   PetscValidIntPointer(numCoveredPoints, 4);
4715   PetscValidPointer(coveredPoints, 5);
4716 
4717   PetscCall(DMPlexGetDepth(dm, &height));
4718   PetscCall(PetscMalloc1(numPoints, &closures));
4719   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4720   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4721   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4722   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4723   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4724 
4725   for (p = 0; p < numPoints; ++p) {
4726     PetscInt closureSize;
4727 
4728     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4729 
4730     offsets[p * (height + 2) + 0] = 0;
4731     for (h = 0; h < height + 1; ++h) {
4732       PetscInt pStart, pEnd, i;
4733 
4734       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4735       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4736         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4737           offsets[p * (height + 2) + h + 1] = i;
4738           break;
4739         }
4740       }
4741       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4742     }
4743     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);
4744   }
4745   for (h = 0; h < height + 1; ++h) {
4746     PetscInt dof;
4747 
4748     /* Copy in cone of first point */
4749     dof = offsets[h + 1] - offsets[h];
4750     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4751     /* Check each successive cone */
4752     for (p = 1; p < numPoints && meetSize; ++p) {
4753       PetscInt newMeetSize = 0;
4754 
4755       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4756       for (c = 0; c < dof; ++c) {
4757         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4758 
4759         for (m = 0; m < meetSize; ++m) {
4760           if (point == meet[i][m]) {
4761             meet[1 - i][newMeetSize++] = point;
4762             break;
4763           }
4764         }
4765       }
4766       meetSize = newMeetSize;
4767       i        = 1 - i;
4768     }
4769     if (meetSize) break;
4770   }
4771   *numCoveredPoints = meetSize;
4772   *coveredPoints    = meet[i];
4773   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4774   PetscCall(PetscFree(closures));
4775   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4776   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4777   PetscFunctionReturn(PETSC_SUCCESS);
4778 }
4779 
4780 /*@C
4781   DMPlexEqual - Determine if two `DM` have the same topology
4782 
4783   Not Collective
4784 
4785   Input Parameters:
4786 + dmA - A `DMPLEX` object
4787 - dmB - A `DMPLEX` object
4788 
4789   Output Parameter:
4790 . equal - `PETSC_TRUE` if the topologies are identical
4791 
4792   Level: intermediate
4793 
4794   Note:
4795   We are not solving graph isomorphism, so we do not permute.
4796 
4797 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4798 @*/
4799 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4800 {
4801   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4802 
4803   PetscFunctionBegin;
4804   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4805   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4806   PetscValidBoolPointer(equal, 3);
4807 
4808   *equal = PETSC_FALSE;
4809   PetscCall(DMPlexGetDepth(dmA, &depth));
4810   PetscCall(DMPlexGetDepth(dmB, &depthB));
4811   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4812   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4813   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4814   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4815   for (p = pStart; p < pEnd; ++p) {
4816     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4817     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4818 
4819     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4820     PetscCall(DMPlexGetCone(dmA, p, &cone));
4821     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4822     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4823     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4824     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4825     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4826     for (c = 0; c < coneSize; ++c) {
4827       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4828       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4829     }
4830     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4831     PetscCall(DMPlexGetSupport(dmA, p, &support));
4832     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4833     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4834     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4835     for (s = 0; s < supportSize; ++s) {
4836       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4837     }
4838   }
4839   *equal = PETSC_TRUE;
4840   PetscFunctionReturn(PETSC_SUCCESS);
4841 }
4842 
4843 /*@C
4844   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4845 
4846   Not Collective
4847 
4848   Input Parameters:
4849 + dm         - The `DMPLEX`
4850 . cellDim    - The cell dimension
4851 - numCorners - The number of vertices on a cell
4852 
4853   Output Parameter:
4854 . numFaceVertices - The number of vertices on a face
4855 
4856   Level: developer
4857 
4858   Note:
4859   Of course this can only work for a restricted set of symmetric shapes
4860 
4861 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4862 @*/
4863 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4864 {
4865   MPI_Comm comm;
4866 
4867   PetscFunctionBegin;
4868   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4869   PetscValidIntPointer(numFaceVertices, 4);
4870   switch (cellDim) {
4871   case 0:
4872     *numFaceVertices = 0;
4873     break;
4874   case 1:
4875     *numFaceVertices = 1;
4876     break;
4877   case 2:
4878     switch (numCorners) {
4879     case 3:                 /* triangle */
4880       *numFaceVertices = 2; /* Edge has 2 vertices */
4881       break;
4882     case 4:                 /* quadrilateral */
4883       *numFaceVertices = 2; /* Edge has 2 vertices */
4884       break;
4885     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4886       *numFaceVertices = 3; /* Edge has 3 vertices */
4887       break;
4888     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4889       *numFaceVertices = 3; /* Edge has 3 vertices */
4890       break;
4891     default:
4892       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4893     }
4894     break;
4895   case 3:
4896     switch (numCorners) {
4897     case 4:                 /* tetradehdron */
4898       *numFaceVertices = 3; /* Face has 3 vertices */
4899       break;
4900     case 6:                 /* tet cohesive cells */
4901       *numFaceVertices = 4; /* Face has 4 vertices */
4902       break;
4903     case 8:                 /* hexahedron */
4904       *numFaceVertices = 4; /* Face has 4 vertices */
4905       break;
4906     case 9:                 /* tet cohesive Lagrange cells */
4907       *numFaceVertices = 6; /* Face has 6 vertices */
4908       break;
4909     case 10:                /* quadratic tetrahedron */
4910       *numFaceVertices = 6; /* Face has 6 vertices */
4911       break;
4912     case 12:                /* hex cohesive Lagrange cells */
4913       *numFaceVertices = 6; /* Face has 6 vertices */
4914       break;
4915     case 18:                /* quadratic tet cohesive Lagrange cells */
4916       *numFaceVertices = 6; /* Face has 6 vertices */
4917       break;
4918     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4919       *numFaceVertices = 9; /* Face has 9 vertices */
4920       break;
4921     default:
4922       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4923     }
4924     break;
4925   default:
4926     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4927   }
4928   PetscFunctionReturn(PETSC_SUCCESS);
4929 }
4930 
4931 /*@
4932   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4933 
4934   Not Collective
4935 
4936   Input Parameter:
4937 . dm    - The `DMPLEX` object
4938 
4939   Output Parameter:
4940 . depthLabel - The `DMLabel` recording point depth
4941 
4942   Level: developer
4943 
4944 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4945 @*/
4946 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4947 {
4948   PetscFunctionBegin;
4949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4950   PetscValidPointer(depthLabel, 2);
4951   *depthLabel = dm->depthLabel;
4952   PetscFunctionReturn(PETSC_SUCCESS);
4953 }
4954 
4955 /*@
4956   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4957 
4958   Not Collective
4959 
4960   Input Parameter:
4961 . dm    - The `DMPLEX` object
4962 
4963   Output Parameter:
4964 . depth - The number of strata (breadth first levels) in the DAG
4965 
4966   Level: developer
4967 
4968   Notes:
4969   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4970 
4971   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4972 
4973   An empty mesh gives -1.
4974 
4975 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4976 @*/
4977 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4978 {
4979   DM_Plex *mesh = (DM_Plex *)dm->data;
4980   DMLabel  label;
4981   PetscInt d = 0;
4982 
4983   PetscFunctionBegin;
4984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4985   PetscValidIntPointer(depth, 2);
4986   if (mesh->tr) {
4987     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4988   } else {
4989     PetscCall(DMPlexGetDepthLabel(dm, &label));
4990     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4991     *depth = d - 1;
4992   }
4993   PetscFunctionReturn(PETSC_SUCCESS);
4994 }
4995 
4996 /*@
4997   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
4998 
4999   Not Collective
5000 
5001   Input Parameters:
5002 + dm    - The `DMPLEX` object
5003 - depth - The requested depth
5004 
5005   Output Parameters:
5006 + start - The first point at this `depth`
5007 - end   - One beyond the last point at this `depth`
5008 
5009   Level: developer
5010 
5011   Notes:
5012   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5013   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5014   higher dimension, e.g., "edges".
5015 
5016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5017 @*/
5018 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5019 {
5020   DM_Plex *mesh = (DM_Plex *)dm->data;
5021   DMLabel  label;
5022   PetscInt pStart, pEnd;
5023 
5024   PetscFunctionBegin;
5025   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5026   if (start) {
5027     PetscValidIntPointer(start, 3);
5028     *start = 0;
5029   }
5030   if (end) {
5031     PetscValidIntPointer(end, 4);
5032     *end = 0;
5033   }
5034   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5035   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5036   if (depth < 0) {
5037     if (start) *start = pStart;
5038     if (end) *end = pEnd;
5039     PetscFunctionReturn(PETSC_SUCCESS);
5040   }
5041   if (mesh->tr) {
5042     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5043   } else {
5044     PetscCall(DMPlexGetDepthLabel(dm, &label));
5045     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5046     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5047   }
5048   PetscFunctionReturn(PETSC_SUCCESS);
5049 }
5050 
5051 /*@
5052   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5053 
5054   Not Collective
5055 
5056   Input Parameters:
5057 + dm     - The `DMPLEX` object
5058 - height - The requested height
5059 
5060   Output Parameters:
5061 + start - The first point at this `height`
5062 - end   - One beyond the last point at this `height`
5063 
5064   Level: developer
5065 
5066   Notes:
5067   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5068   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5069   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5070 
5071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5072 @*/
5073 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5074 {
5075   DMLabel  label;
5076   PetscInt depth, pStart, pEnd;
5077 
5078   PetscFunctionBegin;
5079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5080   if (start) {
5081     PetscValidIntPointer(start, 3);
5082     *start = 0;
5083   }
5084   if (end) {
5085     PetscValidIntPointer(end, 4);
5086     *end = 0;
5087   }
5088   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5089   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5090   if (height < 0) {
5091     if (start) *start = pStart;
5092     if (end) *end = pEnd;
5093     PetscFunctionReturn(PETSC_SUCCESS);
5094   }
5095   PetscCall(DMPlexGetDepthLabel(dm, &label));
5096   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5097   PetscCall(DMLabelGetNumValues(label, &depth));
5098   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5099   PetscFunctionReturn(PETSC_SUCCESS);
5100 }
5101 
5102 /*@
5103   DMPlexGetPointDepth - Get the `depth` of a given point
5104 
5105   Not Collective
5106 
5107   Input Parameters:
5108 + dm    - The `DMPLEX` object
5109 - point - The point
5110 
5111   Output Parameter:
5112 . depth - The depth of the `point`
5113 
5114   Level: intermediate
5115 
5116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5117 @*/
5118 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5119 {
5120   PetscFunctionBegin;
5121   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5122   PetscValidIntPointer(depth, 3);
5123   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5124   PetscFunctionReturn(PETSC_SUCCESS);
5125 }
5126 
5127 /*@
5128   DMPlexGetPointHeight - Get the `height` of a given point
5129 
5130   Not Collective
5131 
5132   Input Parameters:
5133 + dm    - The `DMPLEX` object
5134 - point - The point
5135 
5136   Output Parameter:
5137 . height - The height of the `point`
5138 
5139   Level: intermediate
5140 
5141 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5142 @*/
5143 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5144 {
5145   PetscInt n, pDepth;
5146 
5147   PetscFunctionBegin;
5148   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5149   PetscValidIntPointer(height, 3);
5150   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5151   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5152   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5153   PetscFunctionReturn(PETSC_SUCCESS);
5154 }
5155 
5156 /*@
5157   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5158 
5159   Not Collective
5160 
5161   Input Parameter:
5162 . dm - The `DMPLEX` object
5163 
5164   Output Parameter:
5165 . celltypeLabel - The `DMLabel` recording cell polytope type
5166 
5167   Level: developer
5168 
5169   Note:
5170   This function will trigger automatica computation of cell types. This can be disabled by calling
5171   `DMCreateLabel`(dm, "celltype") beforehand.
5172 
5173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5174 @*/
5175 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5176 {
5177   PetscFunctionBegin;
5178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5179   PetscValidPointer(celltypeLabel, 2);
5180   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5181   *celltypeLabel = dm->celltypeLabel;
5182   PetscFunctionReturn(PETSC_SUCCESS);
5183 }
5184 
5185 /*@
5186   DMPlexGetCellType - Get the polytope type of a given cell
5187 
5188   Not Collective
5189 
5190   Input Parameters:
5191 + dm   - The `DMPLEX` object
5192 - cell - The cell
5193 
5194   Output Parameter:
5195 . celltype - The polytope type of the cell
5196 
5197   Level: intermediate
5198 
5199 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5200 @*/
5201 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5202 {
5203   DM_Plex *mesh = (DM_Plex *)dm->data;
5204   DMLabel  label;
5205   PetscInt ct;
5206 
5207   PetscFunctionBegin;
5208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5209   PetscValidPointer(celltype, 3);
5210   if (mesh->tr) {
5211     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5212   } else {
5213     PetscInt pStart, pEnd;
5214 
5215     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5216     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5217       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5218       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5219       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5220       for (PetscInt p = pStart; p < pEnd; p++) {
5221         PetscCall(DMLabelGetValue(label, p, &ct));
5222         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5223       }
5224     }
5225     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5226     if (PetscDefined(USE_DEBUG)) {
5227       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5228       PetscCall(DMLabelGetValue(label, cell, &ct));
5229       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5230       PetscCheck(ct == *celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5231     }
5232   }
5233   PetscFunctionReturn(PETSC_SUCCESS);
5234 }
5235 
5236 /*@
5237   DMPlexSetCellType - Set the polytope type of a given cell
5238 
5239   Not Collective
5240 
5241   Input Parameters:
5242 + dm   - The `DMPLEX` object
5243 . cell - The cell
5244 - celltype - The polytope type of the cell
5245 
5246   Level: advanced
5247 
5248   Note:
5249   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5250   is executed. This function will override the computed type. However, if automatic classification will not succeed
5251   and a user wants to manually specify all types, the classification must be disabled by calling
5252   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5253 
5254 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5255 @*/
5256 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5257 {
5258   DM_Plex *mesh = (DM_Plex *)dm->data;
5259   DMLabel  label;
5260   PetscInt pStart, pEnd;
5261 
5262   PetscFunctionBegin;
5263   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5264   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5265   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5266   PetscCall(DMLabelSetValue(label, cell, celltype));
5267   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5268   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5269   PetscFunctionReturn(PETSC_SUCCESS);
5270 }
5271 
5272 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5273 {
5274   PetscSection section, s;
5275   Mat          m;
5276   PetscInt     maxHeight;
5277   const char  *prefix;
5278 
5279   PetscFunctionBegin;
5280   PetscCall(DMClone(dm, cdm));
5281   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5282   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5283   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5284   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5285   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5286   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5287   PetscCall(DMSetLocalSection(*cdm, section));
5288   PetscCall(PetscSectionDestroy(&section));
5289   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5290   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5291   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5292   PetscCall(PetscSectionDestroy(&s));
5293   PetscCall(MatDestroy(&m));
5294 
5295   PetscCall(DMSetNumFields(*cdm, 1));
5296   PetscCall(DMCreateDS(*cdm));
5297   (*cdm)->cloneOpts = PETSC_TRUE;
5298   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5299   PetscFunctionReturn(PETSC_SUCCESS);
5300 }
5301 
5302 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5303 {
5304   Vec coordsLocal, cellCoordsLocal;
5305   DM  coordsDM, cellCoordsDM;
5306 
5307   PetscFunctionBegin;
5308   *field = NULL;
5309   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5310   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5311   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5312   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5313   if (coordsLocal && coordsDM) {
5314     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5315     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5316   }
5317   PetscFunctionReturn(PETSC_SUCCESS);
5318 }
5319 
5320 /*@C
5321   DMPlexGetConeSection - Return a section which describes the layout of cone data
5322 
5323   Not Collective
5324 
5325   Input Parameter:
5326 . dm        - The `DMPLEX` object
5327 
5328   Output Parameter:
5329 . section - The `PetscSection` object
5330 
5331   Level: developer
5332 
5333 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5334 @*/
5335 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5336 {
5337   DM_Plex *mesh = (DM_Plex *)dm->data;
5338 
5339   PetscFunctionBegin;
5340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5341   if (section) *section = mesh->coneSection;
5342   PetscFunctionReturn(PETSC_SUCCESS);
5343 }
5344 
5345 /*@C
5346   DMPlexGetSupportSection - Return a section which describes the layout of support data
5347 
5348   Not Collective
5349 
5350   Input Parameter:
5351 . dm        - The `DMPLEX` object
5352 
5353   Output Parameter:
5354 . section - The `PetscSection` object
5355 
5356   Level: developer
5357 
5358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5359 @*/
5360 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5361 {
5362   DM_Plex *mesh = (DM_Plex *)dm->data;
5363 
5364   PetscFunctionBegin;
5365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5366   if (section) *section = mesh->supportSection;
5367   PetscFunctionReturn(PETSC_SUCCESS);
5368 }
5369 
5370 /*@C
5371   DMPlexGetCones - Return cone data
5372 
5373   Not Collective
5374 
5375   Input Parameter:
5376 . dm        - The `DMPLEX` object
5377 
5378   Output Parameter:
5379 . cones - The cone for each point
5380 
5381   Level: developer
5382 
5383 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5384 @*/
5385 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5386 {
5387   DM_Plex *mesh = (DM_Plex *)dm->data;
5388 
5389   PetscFunctionBegin;
5390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5391   if (cones) *cones = mesh->cones;
5392   PetscFunctionReturn(PETSC_SUCCESS);
5393 }
5394 
5395 /*@C
5396   DMPlexGetConeOrientations - Return cone orientation data
5397 
5398   Not Collective
5399 
5400   Input Parameter:
5401 . dm        - The `DMPLEX` object
5402 
5403   Output Parameter:
5404 . coneOrientations - The array of cone orientations for all points
5405 
5406   Level: developer
5407 
5408   Notes:
5409   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5410 
5411   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5412 
5413 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5414 @*/
5415 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5416 {
5417   DM_Plex *mesh = (DM_Plex *)dm->data;
5418 
5419   PetscFunctionBegin;
5420   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5421   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5422   PetscFunctionReturn(PETSC_SUCCESS);
5423 }
5424 
5425 /******************************** FEM Support **********************************/
5426 
5427 /*
5428  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5429  representing a line in the section.
5430 */
5431 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5432 {
5433   PetscFunctionBeginHot;
5434   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5435   if (line < 0) {
5436     *k  = 0;
5437     *Nc = 0;
5438   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5439     *k = 1;
5440   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5441     /* An order k SEM disc has k-1 dofs on an edge */
5442     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5443     *k = *k / *Nc + 1;
5444   }
5445   PetscFunctionReturn(PETSC_SUCCESS);
5446 }
5447 
5448 /*@
5449 
5450   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5451   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5452   section provided (or the section of the `DM`).
5453 
5454   Input Parameters:
5455 + dm      - The `DM`
5456 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5457 - section - The `PetscSection` to reorder, or `NULL` for the default section
5458 
5459   Example:
5460   A typical interpolated single-quad mesh might order points as
5461 .vb
5462   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5463 
5464   v4 -- e6 -- v3
5465   |           |
5466   e7    c0    e8
5467   |           |
5468   v1 -- e5 -- v2
5469 .ve
5470 
5471   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5472   dofs in the order of points, e.g.,
5473 .vb
5474     c0 -> [0,1,2,3]
5475     v1 -> [4]
5476     ...
5477     e5 -> [8, 9]
5478 .ve
5479 
5480   which corresponds to the dofs
5481 .vb
5482     6   10  11  7
5483     13  2   3   15
5484     12  0   1   14
5485     4   8   9   5
5486 .ve
5487 
5488   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5489 .vb
5490   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5491 .ve
5492 
5493   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5494 .vb
5495    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5496 .ve
5497 
5498   Level: developer
5499 
5500   Note:
5501   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5502   degree of the basis.
5503 
5504 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5505 @*/
5506 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5507 {
5508   DMLabel   label;
5509   PetscInt  dim, depth = -1, eStart = -1, Nf;
5510   PetscBool vertexchart;
5511 
5512   PetscFunctionBegin;
5513   PetscCall(DMGetDimension(dm, &dim));
5514   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5515   if (point < 0) {
5516     PetscInt sStart, sEnd;
5517 
5518     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5519     point = sEnd - sStart ? sStart : point;
5520   }
5521   PetscCall(DMPlexGetDepthLabel(dm, &label));
5522   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5523   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5524   if (depth == 1) {
5525     eStart = point;
5526   } else if (depth == dim) {
5527     const PetscInt *cone;
5528 
5529     PetscCall(DMPlexGetCone(dm, point, &cone));
5530     if (dim == 2) eStart = cone[0];
5531     else if (dim == 3) {
5532       const PetscInt *cone2;
5533       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5534       eStart = cone2[0];
5535     } 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);
5536   } 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);
5537   { /* Determine whether the chart covers all points or just vertices. */
5538     PetscInt pStart, pEnd, cStart, cEnd;
5539     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5540     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5541     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5542     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5543     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5544   }
5545   PetscCall(PetscSectionGetNumFields(section, &Nf));
5546   for (PetscInt d = 1; d <= dim; d++) {
5547     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5548     PetscInt *perm;
5549 
5550     for (f = 0; f < Nf; ++f) {
5551       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5552       size += PetscPowInt(k + 1, d) * Nc;
5553     }
5554     PetscCall(PetscMalloc1(size, &perm));
5555     for (f = 0; f < Nf; ++f) {
5556       switch (d) {
5557       case 1:
5558         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5559         /*
5560          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5561          We want              [ vtx0; edge of length k-1; vtx1 ]
5562          */
5563         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5564         for (i = 0; i < k - 1; i++)
5565           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5566         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5567         foffset = offset;
5568         break;
5569       case 2:
5570         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5571         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5572         /* The SEM order is
5573 
5574          v_lb, {e_b}, v_rb,
5575          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5576          v_lt, reverse {e_t}, v_rt
5577          */
5578         {
5579           const PetscInt of   = 0;
5580           const PetscInt oeb  = of + PetscSqr(k - 1);
5581           const PetscInt oer  = oeb + (k - 1);
5582           const PetscInt oet  = oer + (k - 1);
5583           const PetscInt oel  = oet + (k - 1);
5584           const PetscInt ovlb = oel + (k - 1);
5585           const PetscInt ovrb = ovlb + 1;
5586           const PetscInt ovrt = ovrb + 1;
5587           const PetscInt ovlt = ovrt + 1;
5588           PetscInt       o;
5589 
5590           /* bottom */
5591           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5592           for (o = oeb; o < oer; ++o)
5593             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5594           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5595           /* middle */
5596           for (i = 0; i < k - 1; ++i) {
5597             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5598             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5599               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5600             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5601           }
5602           /* top */
5603           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5604           for (o = oel - 1; o >= oet; --o)
5605             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5606           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5607           foffset = offset;
5608         }
5609         break;
5610       case 3:
5611         /* The original hex closure is
5612 
5613          {c,
5614          f_b, f_t, f_f, f_b, f_r, f_l,
5615          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5616          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5617          */
5618         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5619         /* The SEM order is
5620          Bottom Slice
5621          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5622          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5623          v_blb, {e_bb}, v_brb,
5624 
5625          Middle Slice (j)
5626          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5627          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5628          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5629 
5630          Top Slice
5631          v_tlf, {e_tf}, v_trf,
5632          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5633          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5634          */
5635         {
5636           const PetscInt oc    = 0;
5637           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5638           const PetscInt oft   = ofb + PetscSqr(k - 1);
5639           const PetscInt off   = oft + PetscSqr(k - 1);
5640           const PetscInt ofk   = off + PetscSqr(k - 1);
5641           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5642           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5643           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5644           const PetscInt oebb  = oebl + (k - 1);
5645           const PetscInt oebr  = oebb + (k - 1);
5646           const PetscInt oebf  = oebr + (k - 1);
5647           const PetscInt oetf  = oebf + (k - 1);
5648           const PetscInt oetr  = oetf + (k - 1);
5649           const PetscInt oetb  = oetr + (k - 1);
5650           const PetscInt oetl  = oetb + (k - 1);
5651           const PetscInt oerf  = oetl + (k - 1);
5652           const PetscInt oelf  = oerf + (k - 1);
5653           const PetscInt oelb  = oelf + (k - 1);
5654           const PetscInt oerb  = oelb + (k - 1);
5655           const PetscInt ovblf = oerb + (k - 1);
5656           const PetscInt ovblb = ovblf + 1;
5657           const PetscInt ovbrb = ovblb + 1;
5658           const PetscInt ovbrf = ovbrb + 1;
5659           const PetscInt ovtlf = ovbrf + 1;
5660           const PetscInt ovtrf = ovtlf + 1;
5661           const PetscInt ovtrb = ovtrf + 1;
5662           const PetscInt ovtlb = ovtrb + 1;
5663           PetscInt       o, n;
5664 
5665           /* Bottom Slice */
5666           /*   bottom */
5667           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5668           for (o = oetf - 1; o >= oebf; --o)
5669             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5670           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5671           /*   middle */
5672           for (i = 0; i < k - 1; ++i) {
5673             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5674             for (n = 0; n < k - 1; ++n) {
5675               o = ofb + n * (k - 1) + i;
5676               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5677             }
5678             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5679           }
5680           /*   top */
5681           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5682           for (o = oebb; o < oebr; ++o)
5683             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5684           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5685 
5686           /* Middle Slice */
5687           for (j = 0; j < k - 1; ++j) {
5688             /*   bottom */
5689             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5690             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5691               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5692             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5693             /*   middle */
5694             for (i = 0; i < k - 1; ++i) {
5695               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5696               for (n = 0; n < k - 1; ++n)
5697                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5698               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5699             }
5700             /*   top */
5701             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5702             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5703               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5704             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5705           }
5706 
5707           /* Top Slice */
5708           /*   bottom */
5709           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5710           for (o = oetf; o < oetr; ++o)
5711             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5712           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5713           /*   middle */
5714           for (i = 0; i < k - 1; ++i) {
5715             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5716             for (n = 0; n < k - 1; ++n)
5717               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5718             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5719           }
5720           /*   top */
5721           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5722           for (o = oetl - 1; o >= oetb; --o)
5723             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5724           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5725 
5726           foffset = offset;
5727         }
5728         break;
5729       default:
5730         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5731       }
5732     }
5733     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5734     /* Check permutation */
5735     {
5736       PetscInt *check;
5737 
5738       PetscCall(PetscMalloc1(size, &check));
5739       for (i = 0; i < size; ++i) {
5740         check[i] = -1;
5741         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5742       }
5743       for (i = 0; i < size; ++i) check[perm[i]] = i;
5744       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5745       PetscCall(PetscFree(check));
5746     }
5747     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5748     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5749       PetscInt *loc_perm;
5750       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5751       for (PetscInt i = 0; i < size; i++) {
5752         loc_perm[i]        = perm[i];
5753         loc_perm[size + i] = size + perm[i];
5754       }
5755       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5756     }
5757   }
5758   PetscFunctionReturn(PETSC_SUCCESS);
5759 }
5760 
5761 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5762 {
5763   PetscDS  prob;
5764   PetscInt depth, Nf, h;
5765   DMLabel  label;
5766 
5767   PetscFunctionBeginHot;
5768   PetscCall(DMGetDS(dm, &prob));
5769   Nf      = prob->Nf;
5770   label   = dm->depthLabel;
5771   *dspace = NULL;
5772   if (field < Nf) {
5773     PetscObject disc = prob->disc[field];
5774 
5775     if (disc->classid == PETSCFE_CLASSID) {
5776       PetscDualSpace dsp;
5777 
5778       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5779       PetscCall(DMLabelGetNumValues(label, &depth));
5780       PetscCall(DMLabelGetValue(label, point, &h));
5781       h = depth - 1 - h;
5782       if (h) {
5783         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5784       } else {
5785         *dspace = dsp;
5786       }
5787     }
5788   }
5789   PetscFunctionReturn(PETSC_SUCCESS);
5790 }
5791 
5792 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5793 {
5794   PetscScalar       *array;
5795   const PetscScalar *vArray;
5796   const PetscInt    *cone, *coneO;
5797   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5798 
5799   PetscFunctionBeginHot;
5800   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5801   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5802   PetscCall(DMPlexGetCone(dm, point, &cone));
5803   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5804   if (!values || !*values) {
5805     if ((point >= pStart) && (point < pEnd)) {
5806       PetscInt dof;
5807 
5808       PetscCall(PetscSectionGetDof(section, point, &dof));
5809       size += dof;
5810     }
5811     for (p = 0; p < numPoints; ++p) {
5812       const PetscInt cp = cone[p];
5813       PetscInt       dof;
5814 
5815       if ((cp < pStart) || (cp >= pEnd)) continue;
5816       PetscCall(PetscSectionGetDof(section, cp, &dof));
5817       size += dof;
5818     }
5819     if (!values) {
5820       if (csize) *csize = size;
5821       PetscFunctionReturn(PETSC_SUCCESS);
5822     }
5823     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5824   } else {
5825     array = *values;
5826   }
5827   size = 0;
5828   PetscCall(VecGetArrayRead(v, &vArray));
5829   if ((point >= pStart) && (point < pEnd)) {
5830     PetscInt           dof, off, d;
5831     const PetscScalar *varr;
5832 
5833     PetscCall(PetscSectionGetDof(section, point, &dof));
5834     PetscCall(PetscSectionGetOffset(section, point, &off));
5835     varr = &vArray[off];
5836     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5837     size += dof;
5838   }
5839   for (p = 0; p < numPoints; ++p) {
5840     const PetscInt     cp = cone[p];
5841     PetscInt           o  = coneO[p];
5842     PetscInt           dof, off, d;
5843     const PetscScalar *varr;
5844 
5845     if ((cp < pStart) || (cp >= pEnd)) continue;
5846     PetscCall(PetscSectionGetDof(section, cp, &dof));
5847     PetscCall(PetscSectionGetOffset(section, cp, &off));
5848     varr = &vArray[off];
5849     if (o >= 0) {
5850       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5851     } else {
5852       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5853     }
5854     size += dof;
5855   }
5856   PetscCall(VecRestoreArrayRead(v, &vArray));
5857   if (!*values) {
5858     if (csize) *csize = size;
5859     *values = array;
5860   } else {
5861     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5862     *csize = size;
5863   }
5864   PetscFunctionReturn(PETSC_SUCCESS);
5865 }
5866 
5867 /* Compress out points not in the section */
5868 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5869 {
5870   const PetscInt np = *numPoints;
5871   PetscInt       pStart, pEnd, p, q;
5872 
5873   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5874   for (p = 0, q = 0; p < np; ++p) {
5875     const PetscInt r = points[p * 2];
5876     if ((r >= pStart) && (r < pEnd)) {
5877       points[q * 2]     = r;
5878       points[q * 2 + 1] = points[p * 2 + 1];
5879       ++q;
5880     }
5881   }
5882   *numPoints = q;
5883   return PETSC_SUCCESS;
5884 }
5885 
5886 /* Compressed closure does not apply closure permutation */
5887 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5888 {
5889   const PetscInt *cla = NULL;
5890   PetscInt        np, *pts = NULL;
5891 
5892   PetscFunctionBeginHot;
5893   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5894   if (!ornt && *clPoints) {
5895     PetscInt dof, off;
5896 
5897     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5898     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5899     PetscCall(ISGetIndices(*clPoints, &cla));
5900     np  = dof / 2;
5901     pts = (PetscInt *)&cla[off];
5902   } else {
5903     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5904     PetscCall(CompressPoints_Private(section, &np, pts));
5905   }
5906   *numPoints = np;
5907   *points    = pts;
5908   *clp       = cla;
5909   PetscFunctionReturn(PETSC_SUCCESS);
5910 }
5911 
5912 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5913 {
5914   PetscFunctionBeginHot;
5915   if (!*clPoints) {
5916     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5917   } else {
5918     PetscCall(ISRestoreIndices(*clPoints, clp));
5919   }
5920   *numPoints = 0;
5921   *points    = NULL;
5922   *clSec     = NULL;
5923   *clPoints  = NULL;
5924   *clp       = NULL;
5925   PetscFunctionReturn(PETSC_SUCCESS);
5926 }
5927 
5928 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5929 {
5930   PetscInt            offset = 0, p;
5931   const PetscInt    **perms  = NULL;
5932   const PetscScalar **flips  = NULL;
5933 
5934   PetscFunctionBeginHot;
5935   *size = 0;
5936   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5937   for (p = 0; p < numPoints; p++) {
5938     const PetscInt     point = points[2 * p];
5939     const PetscInt    *perm  = perms ? perms[p] : NULL;
5940     const PetscScalar *flip  = flips ? flips[p] : NULL;
5941     PetscInt           dof, off, d;
5942     const PetscScalar *varr;
5943 
5944     PetscCall(PetscSectionGetDof(section, point, &dof));
5945     PetscCall(PetscSectionGetOffset(section, point, &off));
5946     varr = &vArray[off];
5947     if (clperm) {
5948       if (perm) {
5949         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5950       } else {
5951         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5952       }
5953       if (flip) {
5954         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5955       }
5956     } else {
5957       if (perm) {
5958         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5959       } else {
5960         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5961       }
5962       if (flip) {
5963         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5964       }
5965     }
5966     offset += dof;
5967   }
5968   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5969   *size = offset;
5970   PetscFunctionReturn(PETSC_SUCCESS);
5971 }
5972 
5973 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[])
5974 {
5975   PetscInt offset = 0, f;
5976 
5977   PetscFunctionBeginHot;
5978   *size = 0;
5979   for (f = 0; f < numFields; ++f) {
5980     PetscInt            p;
5981     const PetscInt    **perms = NULL;
5982     const PetscScalar **flips = NULL;
5983 
5984     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5985     for (p = 0; p < numPoints; p++) {
5986       const PetscInt     point = points[2 * p];
5987       PetscInt           fdof, foff, b;
5988       const PetscScalar *varr;
5989       const PetscInt    *perm = perms ? perms[p] : NULL;
5990       const PetscScalar *flip = flips ? flips[p] : NULL;
5991 
5992       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5993       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5994       varr = &vArray[foff];
5995       if (clperm) {
5996         if (perm) {
5997           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5998         } else {
5999           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6000         }
6001         if (flip) {
6002           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6003         }
6004       } else {
6005         if (perm) {
6006           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6007         } else {
6008           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6009         }
6010         if (flip) {
6011           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6012         }
6013       }
6014       offset += fdof;
6015     }
6016     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6017   }
6018   *size = offset;
6019   PetscFunctionReturn(PETSC_SUCCESS);
6020 }
6021 
6022 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6023 {
6024   PetscSection    clSection;
6025   IS              clPoints;
6026   PetscInt       *points = NULL;
6027   const PetscInt *clp, *perm;
6028   PetscInt        depth, numFields, numPoints, asize;
6029 
6030   PetscFunctionBeginHot;
6031   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6032   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6033   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6034   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6035   PetscCall(DMPlexGetDepth(dm, &depth));
6036   PetscCall(PetscSectionGetNumFields(section, &numFields));
6037   if (depth == 1 && numFields < 2) {
6038     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6039     PetscFunctionReturn(PETSC_SUCCESS);
6040   }
6041   /* Get points */
6042   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6043   /* Get sizes */
6044   asize = 0;
6045   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6046     PetscInt dof;
6047     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6048     asize += dof;
6049   }
6050   if (values) {
6051     const PetscScalar *vArray;
6052     PetscInt           size;
6053 
6054     if (*values) {
6055       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);
6056     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6057     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6058     PetscCall(VecGetArrayRead(v, &vArray));
6059     /* Get values */
6060     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6061     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6062     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6063     /* Cleanup array */
6064     PetscCall(VecRestoreArrayRead(v, &vArray));
6065   }
6066   if (csize) *csize = asize;
6067   /* Cleanup points */
6068   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6069   PetscFunctionReturn(PETSC_SUCCESS);
6070 }
6071 
6072 /*@C
6073   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6074 
6075   Not collective
6076 
6077   Input Parameters:
6078 + dm - The `DM`
6079 . section - The section describing the layout in `v`, or `NULL` to use the default section
6080 . v - The local vector
6081 - point - The point in the `DM`
6082 
6083   Input/Output Parameters:
6084 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6085 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6086            if the user provided `NULL`, it is a borrowed array and should not be freed
6087 
6088   Level: intermediate
6089 
6090   Notes:
6091   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6092   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6093   assembly function, and a user may already have allocated storage for this operation.
6094 
6095   A typical use could be
6096 .vb
6097    values = NULL;
6098    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6099    for (cl = 0; cl < clSize; ++cl) {
6100      <Compute on closure>
6101    }
6102    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6103 .ve
6104   or
6105 .vb
6106    PetscMalloc1(clMaxSize, &values);
6107    for (p = pStart; p < pEnd; ++p) {
6108      clSize = clMaxSize;
6109      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6110      for (cl = 0; cl < clSize; ++cl) {
6111        <Compute on closure>
6112      }
6113    }
6114    PetscFree(values);
6115 .ve
6116 
6117   Fortran Note:
6118   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6119 
6120 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6121 @*/
6122 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6123 {
6124   PetscFunctionBeginHot;
6125   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6126   PetscFunctionReturn(PETSC_SUCCESS);
6127 }
6128 
6129 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6130 {
6131   DMLabel            depthLabel;
6132   PetscSection       clSection;
6133   IS                 clPoints;
6134   PetscScalar       *array;
6135   const PetscScalar *vArray;
6136   PetscInt          *points = NULL;
6137   const PetscInt    *clp, *perm = NULL;
6138   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6139 
6140   PetscFunctionBeginHot;
6141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6142   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6143   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6144   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6145   PetscCall(DMPlexGetDepth(dm, &mdepth));
6146   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6147   PetscCall(PetscSectionGetNumFields(section, &numFields));
6148   if (mdepth == 1 && numFields < 2) {
6149     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6150     PetscFunctionReturn(PETSC_SUCCESS);
6151   }
6152   /* Get points */
6153   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6154   for (clsize = 0, p = 0; p < Np; p++) {
6155     PetscInt dof;
6156     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6157     clsize += dof;
6158   }
6159   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6160   /* Filter points */
6161   for (p = 0; p < numPoints * 2; p += 2) {
6162     PetscInt dep;
6163 
6164     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6165     if (dep != depth) continue;
6166     points[Np * 2 + 0] = points[p];
6167     points[Np * 2 + 1] = points[p + 1];
6168     ++Np;
6169   }
6170   /* Get array */
6171   if (!values || !*values) {
6172     PetscInt asize = 0, dof;
6173 
6174     for (p = 0; p < Np * 2; p += 2) {
6175       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6176       asize += dof;
6177     }
6178     if (!values) {
6179       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6180       if (csize) *csize = asize;
6181       PetscFunctionReturn(PETSC_SUCCESS);
6182     }
6183     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6184   } else {
6185     array = *values;
6186   }
6187   PetscCall(VecGetArrayRead(v, &vArray));
6188   /* Get values */
6189   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6190   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6191   /* Cleanup points */
6192   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6193   /* Cleanup array */
6194   PetscCall(VecRestoreArrayRead(v, &vArray));
6195   if (!*values) {
6196     if (csize) *csize = size;
6197     *values = array;
6198   } else {
6199     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6200     *csize = size;
6201   }
6202   PetscFunctionReturn(PETSC_SUCCESS);
6203 }
6204 
6205 /*@C
6206   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6207 
6208   Not collective
6209 
6210   Input Parameters:
6211 + dm - The `DM`
6212 . section - The section describing the layout in `v`, or `NULL` to use the default section
6213 . v - The local vector
6214 . point - The point in the `DM`
6215 . csize - The number of values in the closure, or `NULL`
6216 - values - The array of values, which is a borrowed array and should not be freed
6217 
6218   Level: intermediate
6219 
6220   Note:
6221   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6222 
6223   Fortran Note:
6224   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6225 
6226 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6227 @*/
6228 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6229 {
6230   PetscInt size = 0;
6231 
6232   PetscFunctionBegin;
6233   /* Should work without recalculating size */
6234   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6235   *values = NULL;
6236   PetscFunctionReturn(PETSC_SUCCESS);
6237 }
6238 
6239 static inline void add(PetscScalar *x, PetscScalar y)
6240 {
6241   *x += y;
6242 }
6243 static inline void insert(PetscScalar *x, PetscScalar y)
6244 {
6245   *x = y;
6246 }
6247 
6248 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[])
6249 {
6250   PetscInt        cdof;  /* The number of constraints on this point */
6251   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6252   PetscScalar    *a;
6253   PetscInt        off, cind = 0, k;
6254 
6255   PetscFunctionBegin;
6256   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6257   PetscCall(PetscSectionGetOffset(section, point, &off));
6258   a = &array[off];
6259   if (!cdof || setBC) {
6260     if (clperm) {
6261       if (perm) {
6262         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6263       } else {
6264         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6265       }
6266     } else {
6267       if (perm) {
6268         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6269       } else {
6270         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6271       }
6272     }
6273   } else {
6274     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6275     if (clperm) {
6276       if (perm) {
6277         for (k = 0; k < dof; ++k) {
6278           if ((cind < cdof) && (k == cdofs[cind])) {
6279             ++cind;
6280             continue;
6281           }
6282           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6283         }
6284       } else {
6285         for (k = 0; k < dof; ++k) {
6286           if ((cind < cdof) && (k == cdofs[cind])) {
6287             ++cind;
6288             continue;
6289           }
6290           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6291         }
6292       }
6293     } else {
6294       if (perm) {
6295         for (k = 0; k < dof; ++k) {
6296           if ((cind < cdof) && (k == cdofs[cind])) {
6297             ++cind;
6298             continue;
6299           }
6300           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6301         }
6302       } else {
6303         for (k = 0; k < dof; ++k) {
6304           if ((cind < cdof) && (k == cdofs[cind])) {
6305             ++cind;
6306             continue;
6307           }
6308           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6309         }
6310       }
6311     }
6312   }
6313   PetscFunctionReturn(PETSC_SUCCESS);
6314 }
6315 
6316 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[])
6317 {
6318   PetscInt        cdof;  /* The number of constraints on this point */
6319   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6320   PetscScalar    *a;
6321   PetscInt        off, cind = 0, k;
6322 
6323   PetscFunctionBegin;
6324   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6325   PetscCall(PetscSectionGetOffset(section, point, &off));
6326   a = &array[off];
6327   if (cdof) {
6328     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6329     if (clperm) {
6330       if (perm) {
6331         for (k = 0; k < dof; ++k) {
6332           if ((cind < cdof) && (k == cdofs[cind])) {
6333             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6334             cind++;
6335           }
6336         }
6337       } else {
6338         for (k = 0; k < dof; ++k) {
6339           if ((cind < cdof) && (k == cdofs[cind])) {
6340             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6341             cind++;
6342           }
6343         }
6344       }
6345     } else {
6346       if (perm) {
6347         for (k = 0; k < dof; ++k) {
6348           if ((cind < cdof) && (k == cdofs[cind])) {
6349             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6350             cind++;
6351           }
6352         }
6353       } else {
6354         for (k = 0; k < dof; ++k) {
6355           if ((cind < cdof) && (k == cdofs[cind])) {
6356             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6357             cind++;
6358           }
6359         }
6360       }
6361     }
6362   }
6363   PetscFunctionReturn(PETSC_SUCCESS);
6364 }
6365 
6366 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[])
6367 {
6368   PetscScalar    *a;
6369   PetscInt        fdof, foff, fcdof, foffset = *offset;
6370   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6371   PetscInt        cind = 0, b;
6372 
6373   PetscFunctionBegin;
6374   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6375   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6376   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6377   a = &array[foff];
6378   if (!fcdof || setBC) {
6379     if (clperm) {
6380       if (perm) {
6381         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6382       } else {
6383         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6384       }
6385     } else {
6386       if (perm) {
6387         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6388       } else {
6389         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6390       }
6391     }
6392   } else {
6393     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6394     if (clperm) {
6395       if (perm) {
6396         for (b = 0; b < fdof; b++) {
6397           if ((cind < fcdof) && (b == fcdofs[cind])) {
6398             ++cind;
6399             continue;
6400           }
6401           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6402         }
6403       } else {
6404         for (b = 0; b < fdof; b++) {
6405           if ((cind < fcdof) && (b == fcdofs[cind])) {
6406             ++cind;
6407             continue;
6408           }
6409           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6410         }
6411       }
6412     } else {
6413       if (perm) {
6414         for (b = 0; b < fdof; b++) {
6415           if ((cind < fcdof) && (b == fcdofs[cind])) {
6416             ++cind;
6417             continue;
6418           }
6419           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6420         }
6421       } else {
6422         for (b = 0; b < fdof; b++) {
6423           if ((cind < fcdof) && (b == fcdofs[cind])) {
6424             ++cind;
6425             continue;
6426           }
6427           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6428         }
6429       }
6430     }
6431   }
6432   *offset += fdof;
6433   PetscFunctionReturn(PETSC_SUCCESS);
6434 }
6435 
6436 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[])
6437 {
6438   PetscScalar    *a;
6439   PetscInt        fdof, foff, fcdof, foffset = *offset;
6440   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6441   PetscInt        Nc, cind = 0, ncind = 0, b;
6442   PetscBool       ncSet, fcSet;
6443 
6444   PetscFunctionBegin;
6445   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6446   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6447   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6448   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6449   a = &array[foff];
6450   if (fcdof) {
6451     /* We just override fcdof and fcdofs with Ncc and comps */
6452     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6453     if (clperm) {
6454       if (perm) {
6455         if (comps) {
6456           for (b = 0; b < fdof; b++) {
6457             ncSet = fcSet = PETSC_FALSE;
6458             if (b % Nc == comps[ncind]) {
6459               ncind = (ncind + 1) % Ncc;
6460               ncSet = PETSC_TRUE;
6461             }
6462             if ((cind < fcdof) && (b == fcdofs[cind])) {
6463               ++cind;
6464               fcSet = PETSC_TRUE;
6465             }
6466             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6467           }
6468         } else {
6469           for (b = 0; b < fdof; b++) {
6470             if ((cind < fcdof) && (b == fcdofs[cind])) {
6471               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6472               ++cind;
6473             }
6474           }
6475         }
6476       } else {
6477         if (comps) {
6478           for (b = 0; b < fdof; b++) {
6479             ncSet = fcSet = PETSC_FALSE;
6480             if (b % Nc == comps[ncind]) {
6481               ncind = (ncind + 1) % Ncc;
6482               ncSet = PETSC_TRUE;
6483             }
6484             if ((cind < fcdof) && (b == fcdofs[cind])) {
6485               ++cind;
6486               fcSet = PETSC_TRUE;
6487             }
6488             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6489           }
6490         } else {
6491           for (b = 0; b < fdof; b++) {
6492             if ((cind < fcdof) && (b == fcdofs[cind])) {
6493               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6494               ++cind;
6495             }
6496           }
6497         }
6498       }
6499     } else {
6500       if (perm) {
6501         if (comps) {
6502           for (b = 0; b < fdof; b++) {
6503             ncSet = fcSet = PETSC_FALSE;
6504             if (b % Nc == comps[ncind]) {
6505               ncind = (ncind + 1) % Ncc;
6506               ncSet = PETSC_TRUE;
6507             }
6508             if ((cind < fcdof) && (b == fcdofs[cind])) {
6509               ++cind;
6510               fcSet = PETSC_TRUE;
6511             }
6512             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6513           }
6514         } else {
6515           for (b = 0; b < fdof; b++) {
6516             if ((cind < fcdof) && (b == fcdofs[cind])) {
6517               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6518               ++cind;
6519             }
6520           }
6521         }
6522       } else {
6523         if (comps) {
6524           for (b = 0; b < fdof; b++) {
6525             ncSet = fcSet = PETSC_FALSE;
6526             if (b % Nc == comps[ncind]) {
6527               ncind = (ncind + 1) % Ncc;
6528               ncSet = PETSC_TRUE;
6529             }
6530             if ((cind < fcdof) && (b == fcdofs[cind])) {
6531               ++cind;
6532               fcSet = PETSC_TRUE;
6533             }
6534             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6535           }
6536         } else {
6537           for (b = 0; b < fdof; b++) {
6538             if ((cind < fcdof) && (b == fcdofs[cind])) {
6539               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6540               ++cind;
6541             }
6542           }
6543         }
6544       }
6545     }
6546   }
6547   *offset += fdof;
6548   PetscFunctionReturn(PETSC_SUCCESS);
6549 }
6550 
6551 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6552 {
6553   PetscScalar    *array;
6554   const PetscInt *cone, *coneO;
6555   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6556 
6557   PetscFunctionBeginHot;
6558   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6559   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6560   PetscCall(DMPlexGetCone(dm, point, &cone));
6561   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6562   PetscCall(VecGetArray(v, &array));
6563   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6564     const PetscInt cp = !p ? point : cone[p - 1];
6565     const PetscInt o  = !p ? 0 : coneO[p - 1];
6566 
6567     if ((cp < pStart) || (cp >= pEnd)) {
6568       dof = 0;
6569       continue;
6570     }
6571     PetscCall(PetscSectionGetDof(section, cp, &dof));
6572     /* ADD_VALUES */
6573     {
6574       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6575       PetscScalar    *a;
6576       PetscInt        cdof, coff, cind = 0, k;
6577 
6578       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6579       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6580       a = &array[coff];
6581       if (!cdof) {
6582         if (o >= 0) {
6583           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6584         } else {
6585           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6586         }
6587       } else {
6588         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6589         if (o >= 0) {
6590           for (k = 0; k < dof; ++k) {
6591             if ((cind < cdof) && (k == cdofs[cind])) {
6592               ++cind;
6593               continue;
6594             }
6595             a[k] += values[off + k];
6596           }
6597         } else {
6598           for (k = 0; k < dof; ++k) {
6599             if ((cind < cdof) && (k == cdofs[cind])) {
6600               ++cind;
6601               continue;
6602             }
6603             a[k] += values[off + dof - k - 1];
6604           }
6605         }
6606       }
6607     }
6608   }
6609   PetscCall(VecRestoreArray(v, &array));
6610   PetscFunctionReturn(PETSC_SUCCESS);
6611 }
6612 
6613 /*@C
6614   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6615 
6616   Not collective
6617 
6618   Input Parameters:
6619 + dm - The `DM`
6620 . section - The section describing the layout in `v`, or `NULL` to use the default section
6621 . v - The local vector
6622 . point - The point in the `DM`
6623 . values - The array of values
6624 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6625          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6626 
6627   Level: intermediate
6628 
6629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6630 @*/
6631 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6632 {
6633   PetscSection    clSection;
6634   IS              clPoints;
6635   PetscScalar    *array;
6636   PetscInt       *points = NULL;
6637   const PetscInt *clp, *clperm = NULL;
6638   PetscInt        depth, numFields, numPoints, p, clsize;
6639 
6640   PetscFunctionBeginHot;
6641   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6642   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6643   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6644   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6645   PetscCall(DMPlexGetDepth(dm, &depth));
6646   PetscCall(PetscSectionGetNumFields(section, &numFields));
6647   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6648     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6649     PetscFunctionReturn(PETSC_SUCCESS);
6650   }
6651   /* Get points */
6652   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6653   for (clsize = 0, p = 0; p < numPoints; p++) {
6654     PetscInt dof;
6655     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6656     clsize += dof;
6657   }
6658   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6659   /* Get array */
6660   PetscCall(VecGetArray(v, &array));
6661   /* Get values */
6662   if (numFields > 0) {
6663     PetscInt offset = 0, f;
6664     for (f = 0; f < numFields; ++f) {
6665       const PetscInt    **perms = NULL;
6666       const PetscScalar **flips = NULL;
6667 
6668       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6669       switch (mode) {
6670       case INSERT_VALUES:
6671         for (p = 0; p < numPoints; p++) {
6672           const PetscInt     point = points[2 * p];
6673           const PetscInt    *perm  = perms ? perms[p] : NULL;
6674           const PetscScalar *flip  = flips ? flips[p] : NULL;
6675           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6676         }
6677         break;
6678       case INSERT_ALL_VALUES:
6679         for (p = 0; p < numPoints; p++) {
6680           const PetscInt     point = points[2 * p];
6681           const PetscInt    *perm  = perms ? perms[p] : NULL;
6682           const PetscScalar *flip  = flips ? flips[p] : NULL;
6683           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6684         }
6685         break;
6686       case INSERT_BC_VALUES:
6687         for (p = 0; p < numPoints; p++) {
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(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6692         }
6693         break;
6694       case ADD_VALUES:
6695         for (p = 0; p < numPoints; p++) {
6696           const PetscInt     point = points[2 * p];
6697           const PetscInt    *perm  = perms ? perms[p] : NULL;
6698           const PetscScalar *flip  = flips ? flips[p] : NULL;
6699           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6700         }
6701         break;
6702       case ADD_ALL_VALUES:
6703         for (p = 0; p < numPoints; p++) {
6704           const PetscInt     point = points[2 * p];
6705           const PetscInt    *perm  = perms ? perms[p] : NULL;
6706           const PetscScalar *flip  = flips ? flips[p] : NULL;
6707           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6708         }
6709         break;
6710       case ADD_BC_VALUES:
6711         for (p = 0; p < numPoints; p++) {
6712           const PetscInt     point = points[2 * p];
6713           const PetscInt    *perm  = perms ? perms[p] : NULL;
6714           const PetscScalar *flip  = flips ? flips[p] : NULL;
6715           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6716         }
6717         break;
6718       default:
6719         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6720       }
6721       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6722     }
6723   } else {
6724     PetscInt            dof, off;
6725     const PetscInt    **perms = NULL;
6726     const PetscScalar **flips = NULL;
6727 
6728     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6729     switch (mode) {
6730     case INSERT_VALUES:
6731       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6732         const PetscInt     point = points[2 * p];
6733         const PetscInt    *perm  = perms ? perms[p] : NULL;
6734         const PetscScalar *flip  = flips ? flips[p] : NULL;
6735         PetscCall(PetscSectionGetDof(section, point, &dof));
6736         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6737       }
6738       break;
6739     case INSERT_ALL_VALUES:
6740       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6741         const PetscInt     point = points[2 * p];
6742         const PetscInt    *perm  = perms ? perms[p] : NULL;
6743         const PetscScalar *flip  = flips ? flips[p] : NULL;
6744         PetscCall(PetscSectionGetDof(section, point, &dof));
6745         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6746       }
6747       break;
6748     case INSERT_BC_VALUES:
6749       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6750         const PetscInt     point = points[2 * p];
6751         const PetscInt    *perm  = perms ? perms[p] : NULL;
6752         const PetscScalar *flip  = flips ? flips[p] : NULL;
6753         PetscCall(PetscSectionGetDof(section, point, &dof));
6754         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6755       }
6756       break;
6757     case ADD_VALUES:
6758       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6759         const PetscInt     point = points[2 * p];
6760         const PetscInt    *perm  = perms ? perms[p] : NULL;
6761         const PetscScalar *flip  = flips ? flips[p] : NULL;
6762         PetscCall(PetscSectionGetDof(section, point, &dof));
6763         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6764       }
6765       break;
6766     case ADD_ALL_VALUES:
6767       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6768         const PetscInt     point = points[2 * p];
6769         const PetscInt    *perm  = perms ? perms[p] : NULL;
6770         const PetscScalar *flip  = flips ? flips[p] : NULL;
6771         PetscCall(PetscSectionGetDof(section, point, &dof));
6772         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6773       }
6774       break;
6775     case ADD_BC_VALUES:
6776       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6777         const PetscInt     point = points[2 * p];
6778         const PetscInt    *perm  = perms ? perms[p] : NULL;
6779         const PetscScalar *flip  = flips ? flips[p] : NULL;
6780         PetscCall(PetscSectionGetDof(section, point, &dof));
6781         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6782       }
6783       break;
6784     default:
6785       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6786     }
6787     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6788   }
6789   /* Cleanup points */
6790   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6791   /* Cleanup array */
6792   PetscCall(VecRestoreArray(v, &array));
6793   PetscFunctionReturn(PETSC_SUCCESS);
6794 }
6795 
6796 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6797 {
6798   const PetscInt *supp, *cone;
6799   PetscScalar    *a;
6800   PetscInt        dim, Ns, dof, off, n = 0;
6801 
6802   PetscFunctionBegin;
6803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6804   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6805   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6806   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6807   if (PetscDefined(USE_DEBUG)) {
6808     PetscInt vStart, vEnd;
6809 
6810     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6811     PetscCheck(point >= vStart && point < vEnd, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " must be a vertex in [%" PetscInt_FMT ", %" PetscInt_FMT "]", point, vStart, vEnd);
6812   }
6813   PetscValidScalarPointer(values, 5);
6814 
6815   PetscCall(DMGetDimension(dm, &dim));
6816   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6817   PetscCall(DMPlexGetSupport(dm, point, &supp));
6818   PetscCheck(Ns == 2 * dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " has support size %" PetscInt_FMT " != %" PetscInt_FMT, point, Ns, 2 * dim);
6819   PetscCall(VecGetArray(v, &a));
6820   PetscCall(PetscSectionGetDof(section, point, &dof));
6821   PetscCall(PetscSectionGetOffset(section, point, &off));
6822   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6823   for (PetscInt d = 0; d < dim; ++d) {
6824     // Left edge
6825     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6826     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6827     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6828     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6829     // Right edge
6830     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6831     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6832     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6833     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6834   }
6835   PetscCall(VecRestoreArray(v, &a));
6836   PetscFunctionReturn(PETSC_SUCCESS);
6837 }
6838 
6839 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6840 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6841 {
6842   PetscFunctionBegin;
6843   *contains = PETSC_TRUE;
6844   if (label) {
6845     PetscInt fdof;
6846 
6847     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6848     if (!*contains) {
6849       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6850       *offset += fdof;
6851       PetscFunctionReturn(PETSC_SUCCESS);
6852     }
6853   }
6854   PetscFunctionReturn(PETSC_SUCCESS);
6855 }
6856 
6857 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6858 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)
6859 {
6860   PetscSection    clSection;
6861   IS              clPoints;
6862   PetscScalar    *array;
6863   PetscInt       *points = NULL;
6864   const PetscInt *clp;
6865   PetscInt        numFields, numPoints, p;
6866   PetscInt        offset = 0, f;
6867 
6868   PetscFunctionBeginHot;
6869   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6870   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6871   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6872   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6873   PetscCall(PetscSectionGetNumFields(section, &numFields));
6874   /* Get points */
6875   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6876   /* Get array */
6877   PetscCall(VecGetArray(v, &array));
6878   /* Get values */
6879   for (f = 0; f < numFields; ++f) {
6880     const PetscInt    **perms = NULL;
6881     const PetscScalar **flips = NULL;
6882     PetscBool           contains;
6883 
6884     if (!fieldActive[f]) {
6885       for (p = 0; p < numPoints * 2; p += 2) {
6886         PetscInt fdof;
6887         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6888         offset += fdof;
6889       }
6890       continue;
6891     }
6892     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6893     switch (mode) {
6894     case INSERT_VALUES:
6895       for (p = 0; p < numPoints; p++) {
6896         const PetscInt     point = points[2 * p];
6897         const PetscInt    *perm  = perms ? perms[p] : NULL;
6898         const PetscScalar *flip  = flips ? flips[p] : NULL;
6899         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6900         if (!contains) continue;
6901         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6902       }
6903       break;
6904     case INSERT_ALL_VALUES:
6905       for (p = 0; p < numPoints; p++) {
6906         const PetscInt     point = points[2 * p];
6907         const PetscInt    *perm  = perms ? perms[p] : NULL;
6908         const PetscScalar *flip  = flips ? flips[p] : NULL;
6909         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6910         if (!contains) continue;
6911         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6912       }
6913       break;
6914     case INSERT_BC_VALUES:
6915       for (p = 0; p < numPoints; p++) {
6916         const PetscInt     point = points[2 * p];
6917         const PetscInt    *perm  = perms ? perms[p] : NULL;
6918         const PetscScalar *flip  = flips ? flips[p] : NULL;
6919         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6920         if (!contains) continue;
6921         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6922       }
6923       break;
6924     case ADD_VALUES:
6925       for (p = 0; p < numPoints; p++) {
6926         const PetscInt     point = points[2 * p];
6927         const PetscInt    *perm  = perms ? perms[p] : NULL;
6928         const PetscScalar *flip  = flips ? flips[p] : NULL;
6929         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6930         if (!contains) continue;
6931         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6932       }
6933       break;
6934     case ADD_ALL_VALUES:
6935       for (p = 0; p < numPoints; p++) {
6936         const PetscInt     point = points[2 * p];
6937         const PetscInt    *perm  = perms ? perms[p] : NULL;
6938         const PetscScalar *flip  = flips ? flips[p] : NULL;
6939         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6940         if (!contains) continue;
6941         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6942       }
6943       break;
6944     default:
6945       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6946     }
6947     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6948   }
6949   /* Cleanup points */
6950   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6951   /* Cleanup array */
6952   PetscCall(VecRestoreArray(v, &array));
6953   PetscFunctionReturn(PETSC_SUCCESS);
6954 }
6955 
6956 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6957 {
6958   PetscMPIInt rank;
6959   PetscInt    i, j;
6960 
6961   PetscFunctionBegin;
6962   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6963   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6964   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6965   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6966   numCIndices = numCIndices ? numCIndices : numRIndices;
6967   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6968   for (i = 0; i < numRIndices; i++) {
6969     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6970     for (j = 0; j < numCIndices; j++) {
6971 #if defined(PETSC_USE_COMPLEX)
6972       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6973 #else
6974       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6975 #endif
6976     }
6977     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6978   }
6979   PetscFunctionReturn(PETSC_SUCCESS);
6980 }
6981 
6982 /*
6983   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6984 
6985   Input Parameters:
6986 + section - The section for this data layout
6987 . islocal - Is the section (and thus indices being requested) local or global?
6988 . point   - The point contributing dofs with these indices
6989 . off     - The global offset of this point
6990 . loff    - The local offset of each field
6991 . setBC   - The flag determining whether to include indices of boundary values
6992 . perm    - A permutation of the dofs on this point, or NULL
6993 - indperm - A permutation of the entire indices array, or NULL
6994 
6995   Output Parameter:
6996 . indices - Indices for dofs on this point
6997 
6998   Level: developer
6999 
7000   Note: The indices could be local or global, depending on the value of 'off'.
7001 */
7002 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7003 {
7004   PetscInt        dof;   /* The number of unknowns on this point */
7005   PetscInt        cdof;  /* The number of constraints on this point */
7006   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7007   PetscInt        cind = 0, k;
7008 
7009   PetscFunctionBegin;
7010   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7011   PetscCall(PetscSectionGetDof(section, point, &dof));
7012   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7013   if (!cdof || setBC) {
7014     for (k = 0; k < dof; ++k) {
7015       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7016       const PetscInt ind    = indperm ? indperm[preind] : preind;
7017 
7018       indices[ind] = off + k;
7019     }
7020   } else {
7021     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7022     for (k = 0; k < dof; ++k) {
7023       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7024       const PetscInt ind    = indperm ? indperm[preind] : preind;
7025 
7026       if ((cind < cdof) && (k == cdofs[cind])) {
7027         /* Insert check for returning constrained indices */
7028         indices[ind] = -(off + k + 1);
7029         ++cind;
7030       } else {
7031         indices[ind] = off + k - (islocal ? 0 : cind);
7032       }
7033     }
7034   }
7035   *loff += dof;
7036   PetscFunctionReturn(PETSC_SUCCESS);
7037 }
7038 
7039 /*
7040  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7041 
7042  Input Parameters:
7043 + section - a section (global or local)
7044 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7045 . point - point within section
7046 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7047 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7048 . setBC - identify constrained (boundary condition) points via involution.
7049 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7050 . permsoff - offset
7051 - indperm - index permutation
7052 
7053  Output Parameter:
7054 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7055 . indices - array to hold indices (as defined by section) of each dof associated with point
7056 
7057  Notes:
7058  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7059  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7060  in the local vector.
7061 
7062  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7063  significant).  It is invalid to call with a global section and setBC=true.
7064 
7065  Developer Note:
7066  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7067  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7068  offset could be obtained from the section instead of passing it explicitly as we do now.
7069 
7070  Example:
7071  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7072  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7073  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7074  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.
7075 
7076  Level: developer
7077 */
7078 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[])
7079 {
7080   PetscInt numFields, foff, f;
7081 
7082   PetscFunctionBegin;
7083   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7084   PetscCall(PetscSectionGetNumFields(section, &numFields));
7085   for (f = 0, foff = 0; f < numFields; ++f) {
7086     PetscInt        fdof, cfdof;
7087     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7088     PetscInt        cind = 0, b;
7089     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7090 
7091     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7092     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7093     if (!cfdof || setBC) {
7094       for (b = 0; b < fdof; ++b) {
7095         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7096         const PetscInt ind    = indperm ? indperm[preind] : preind;
7097 
7098         indices[ind] = off + foff + b;
7099       }
7100     } else {
7101       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7102       for (b = 0; b < fdof; ++b) {
7103         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7104         const PetscInt ind    = indperm ? indperm[preind] : preind;
7105 
7106         if ((cind < cfdof) && (b == fcdofs[cind])) {
7107           indices[ind] = -(off + foff + b + 1);
7108           ++cind;
7109         } else {
7110           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7111         }
7112       }
7113     }
7114     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7115     foffs[f] += fdof;
7116   }
7117   PetscFunctionReturn(PETSC_SUCCESS);
7118 }
7119 
7120 /*
7121   This version believes the globalSection offsets for each field, rather than just the point offset
7122 
7123  . foffs - The offset into 'indices' for each field, since it is segregated by field
7124 
7125  Notes:
7126  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7127  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7128 */
7129 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7130 {
7131   PetscInt numFields, foff, f;
7132 
7133   PetscFunctionBegin;
7134   PetscCall(PetscSectionGetNumFields(section, &numFields));
7135   for (f = 0; f < numFields; ++f) {
7136     PetscInt        fdof, cfdof;
7137     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7138     PetscInt        cind = 0, b;
7139     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7140 
7141     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7142     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7143     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7144     if (!cfdof) {
7145       for (b = 0; b < fdof; ++b) {
7146         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7147         const PetscInt ind    = indperm ? indperm[preind] : preind;
7148 
7149         indices[ind] = foff + b;
7150       }
7151     } else {
7152       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7153       for (b = 0; b < fdof; ++b) {
7154         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7155         const PetscInt ind    = indperm ? indperm[preind] : preind;
7156 
7157         if ((cind < cfdof) && (b == fcdofs[cind])) {
7158           indices[ind] = -(foff + b + 1);
7159           ++cind;
7160         } else {
7161           indices[ind] = foff + b - cind;
7162         }
7163       }
7164     }
7165     foffs[f] += fdof;
7166   }
7167   PetscFunctionReturn(PETSC_SUCCESS);
7168 }
7169 
7170 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)
7171 {
7172   Mat             cMat;
7173   PetscSection    aSec, cSec;
7174   IS              aIS;
7175   PetscInt        aStart = -1, aEnd = -1;
7176   const PetscInt *anchors;
7177   PetscInt        numFields, f, p, q, newP = 0;
7178   PetscInt        newNumPoints = 0, newNumIndices = 0;
7179   PetscInt       *newPoints, *indices, *newIndices;
7180   PetscInt        maxAnchor, maxDof;
7181   PetscInt        newOffsets[32];
7182   PetscInt       *pointMatOffsets[32];
7183   PetscInt       *newPointOffsets[32];
7184   PetscScalar    *pointMat[32];
7185   PetscScalar    *newValues      = NULL, *tmpValues;
7186   PetscBool       anyConstrained = PETSC_FALSE;
7187 
7188   PetscFunctionBegin;
7189   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7190   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7191   PetscCall(PetscSectionGetNumFields(section, &numFields));
7192 
7193   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7194   /* if there are point-to-point constraints */
7195   if (aSec) {
7196     PetscCall(PetscArrayzero(newOffsets, 32));
7197     PetscCall(ISGetIndices(aIS, &anchors));
7198     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7199     /* figure out how many points are going to be in the new element matrix
7200      * (we allow double counting, because it's all just going to be summed
7201      * into the global matrix anyway) */
7202     for (p = 0; p < 2 * numPoints; p += 2) {
7203       PetscInt b    = points[p];
7204       PetscInt bDof = 0, bSecDof;
7205 
7206       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7207       if (!bSecDof) continue;
7208       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7209       if (bDof) {
7210         /* this point is constrained */
7211         /* it is going to be replaced by its anchors */
7212         PetscInt bOff, q;
7213 
7214         anyConstrained = PETSC_TRUE;
7215         newNumPoints += bDof;
7216         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7217         for (q = 0; q < bDof; q++) {
7218           PetscInt a = anchors[bOff + q];
7219           PetscInt aDof;
7220 
7221           PetscCall(PetscSectionGetDof(section, a, &aDof));
7222           newNumIndices += aDof;
7223           for (f = 0; f < numFields; ++f) {
7224             PetscInt fDof;
7225 
7226             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7227             newOffsets[f + 1] += fDof;
7228           }
7229         }
7230       } else {
7231         /* this point is not constrained */
7232         newNumPoints++;
7233         newNumIndices += bSecDof;
7234         for (f = 0; f < numFields; ++f) {
7235           PetscInt fDof;
7236 
7237           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7238           newOffsets[f + 1] += fDof;
7239         }
7240       }
7241     }
7242   }
7243   if (!anyConstrained) {
7244     if (outNumPoints) *outNumPoints = 0;
7245     if (outNumIndices) *outNumIndices = 0;
7246     if (outPoints) *outPoints = NULL;
7247     if (outValues) *outValues = NULL;
7248     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7249     PetscFunctionReturn(PETSC_SUCCESS);
7250   }
7251 
7252   if (outNumPoints) *outNumPoints = newNumPoints;
7253   if (outNumIndices) *outNumIndices = newNumIndices;
7254 
7255   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7256 
7257   if (!outPoints && !outValues) {
7258     if (offsets) {
7259       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7260     }
7261     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7262     PetscFunctionReturn(PETSC_SUCCESS);
7263   }
7264 
7265   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7266 
7267   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7268 
7269   /* workspaces */
7270   if (numFields) {
7271     for (f = 0; f < numFields; f++) {
7272       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7273       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7274     }
7275   } else {
7276     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7277     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7278   }
7279 
7280   /* get workspaces for the point-to-point matrices */
7281   if (numFields) {
7282     PetscInt totalOffset, totalMatOffset;
7283 
7284     for (p = 0; p < numPoints; p++) {
7285       PetscInt b    = points[2 * p];
7286       PetscInt bDof = 0, bSecDof;
7287 
7288       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7289       if (!bSecDof) {
7290         for (f = 0; f < numFields; f++) {
7291           newPointOffsets[f][p + 1] = 0;
7292           pointMatOffsets[f][p + 1] = 0;
7293         }
7294         continue;
7295       }
7296       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7297       if (bDof) {
7298         for (f = 0; f < numFields; f++) {
7299           PetscInt fDof, q, bOff, allFDof = 0;
7300 
7301           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7302           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7303           for (q = 0; q < bDof; q++) {
7304             PetscInt a = anchors[bOff + q];
7305             PetscInt aFDof;
7306 
7307             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7308             allFDof += aFDof;
7309           }
7310           newPointOffsets[f][p + 1] = allFDof;
7311           pointMatOffsets[f][p + 1] = fDof * allFDof;
7312         }
7313       } else {
7314         for (f = 0; f < numFields; f++) {
7315           PetscInt fDof;
7316 
7317           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7318           newPointOffsets[f][p + 1] = fDof;
7319           pointMatOffsets[f][p + 1] = 0;
7320         }
7321       }
7322     }
7323     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7324       newPointOffsets[f][0] = totalOffset;
7325       pointMatOffsets[f][0] = totalMatOffset;
7326       for (p = 0; p < numPoints; p++) {
7327         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7328         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7329       }
7330       totalOffset    = newPointOffsets[f][numPoints];
7331       totalMatOffset = pointMatOffsets[f][numPoints];
7332       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7333     }
7334   } else {
7335     for (p = 0; p < numPoints; p++) {
7336       PetscInt b    = points[2 * p];
7337       PetscInt bDof = 0, bSecDof;
7338 
7339       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7340       if (!bSecDof) {
7341         newPointOffsets[0][p + 1] = 0;
7342         pointMatOffsets[0][p + 1] = 0;
7343         continue;
7344       }
7345       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7346       if (bDof) {
7347         PetscInt bOff, q, allDof = 0;
7348 
7349         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7350         for (q = 0; q < bDof; q++) {
7351           PetscInt a = anchors[bOff + q], aDof;
7352 
7353           PetscCall(PetscSectionGetDof(section, a, &aDof));
7354           allDof += aDof;
7355         }
7356         newPointOffsets[0][p + 1] = allDof;
7357         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7358       } else {
7359         newPointOffsets[0][p + 1] = bSecDof;
7360         pointMatOffsets[0][p + 1] = 0;
7361       }
7362     }
7363     newPointOffsets[0][0] = 0;
7364     pointMatOffsets[0][0] = 0;
7365     for (p = 0; p < numPoints; p++) {
7366       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7367       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7368     }
7369     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7370   }
7371 
7372   /* output arrays */
7373   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7374 
7375   /* get the point-to-point matrices; construct newPoints */
7376   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7377   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7378   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7379   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7380   if (numFields) {
7381     for (p = 0, newP = 0; p < numPoints; p++) {
7382       PetscInt b    = points[2 * p];
7383       PetscInt o    = points[2 * p + 1];
7384       PetscInt bDof = 0, bSecDof;
7385 
7386       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7387       if (!bSecDof) continue;
7388       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7389       if (bDof) {
7390         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7391 
7392         fStart[0] = 0;
7393         fEnd[0]   = 0;
7394         for (f = 0; f < numFields; f++) {
7395           PetscInt fDof;
7396 
7397           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7398           fStart[f + 1] = fStart[f] + fDof;
7399           fEnd[f + 1]   = fStart[f + 1];
7400         }
7401         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7402         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7403 
7404         fAnchorStart[0] = 0;
7405         fAnchorEnd[0]   = 0;
7406         for (f = 0; f < numFields; f++) {
7407           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7408 
7409           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7410           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7411         }
7412         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7413         for (q = 0; q < bDof; q++) {
7414           PetscInt a = anchors[bOff + q], aOff;
7415 
7416           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7417           newPoints[2 * (newP + q)]     = a;
7418           newPoints[2 * (newP + q) + 1] = 0;
7419           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7420           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7421         }
7422         newP += bDof;
7423 
7424         if (outValues) {
7425           /* get the point-to-point submatrix */
7426           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]));
7427         }
7428       } else {
7429         newPoints[2 * newP]     = b;
7430         newPoints[2 * newP + 1] = o;
7431         newP++;
7432       }
7433     }
7434   } else {
7435     for (p = 0; p < numPoints; p++) {
7436       PetscInt b    = points[2 * p];
7437       PetscInt o    = points[2 * p + 1];
7438       PetscInt bDof = 0, bSecDof;
7439 
7440       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7441       if (!bSecDof) continue;
7442       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7443       if (bDof) {
7444         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7445 
7446         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7447         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7448 
7449         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7450         for (q = 0; q < bDof; q++) {
7451           PetscInt a = anchors[bOff + q], aOff;
7452 
7453           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7454 
7455           newPoints[2 * (newP + q)]     = a;
7456           newPoints[2 * (newP + q) + 1] = 0;
7457           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7458           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7459         }
7460         newP += bDof;
7461 
7462         /* get the point-to-point submatrix */
7463         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7464       } else {
7465         newPoints[2 * newP]     = b;
7466         newPoints[2 * newP + 1] = o;
7467         newP++;
7468       }
7469     }
7470   }
7471 
7472   if (outValues) {
7473     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7474     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7475     /* multiply constraints on the right */
7476     if (numFields) {
7477       for (f = 0; f < numFields; f++) {
7478         PetscInt oldOff = offsets[f];
7479 
7480         for (p = 0; p < numPoints; p++) {
7481           PetscInt cStart = newPointOffsets[f][p];
7482           PetscInt b      = points[2 * p];
7483           PetscInt c, r, k;
7484           PetscInt dof;
7485 
7486           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7487           if (!dof) continue;
7488           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7489             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7490             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7491 
7492             for (r = 0; r < numIndices; r++) {
7493               for (c = 0; c < nCols; c++) {
7494                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7495               }
7496             }
7497           } else {
7498             /* copy this column as is */
7499             for (r = 0; r < numIndices; r++) {
7500               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7501             }
7502           }
7503           oldOff += dof;
7504         }
7505       }
7506     } else {
7507       PetscInt oldOff = 0;
7508       for (p = 0; p < numPoints; p++) {
7509         PetscInt cStart = newPointOffsets[0][p];
7510         PetscInt b      = points[2 * p];
7511         PetscInt c, r, k;
7512         PetscInt dof;
7513 
7514         PetscCall(PetscSectionGetDof(section, b, &dof));
7515         if (!dof) continue;
7516         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7517           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7518           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7519 
7520           for (r = 0; r < numIndices; r++) {
7521             for (c = 0; c < nCols; c++) {
7522               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7523             }
7524           }
7525         } else {
7526           /* copy this column as is */
7527           for (r = 0; r < numIndices; r++) {
7528             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7529           }
7530         }
7531         oldOff += dof;
7532       }
7533     }
7534 
7535     if (multiplyLeft) {
7536       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7537       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7538       /* multiply constraints transpose on the left */
7539       if (numFields) {
7540         for (f = 0; f < numFields; f++) {
7541           PetscInt oldOff = offsets[f];
7542 
7543           for (p = 0; p < numPoints; p++) {
7544             PetscInt rStart = newPointOffsets[f][p];
7545             PetscInt b      = points[2 * p];
7546             PetscInt c, r, k;
7547             PetscInt dof;
7548 
7549             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7550             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7551               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7552               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7553 
7554               for (r = 0; r < nRows; r++) {
7555                 for (c = 0; c < newNumIndices; c++) {
7556                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7557                 }
7558               }
7559             } else {
7560               /* copy this row as is */
7561               for (r = 0; r < dof; r++) {
7562                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7563               }
7564             }
7565             oldOff += dof;
7566           }
7567         }
7568       } else {
7569         PetscInt oldOff = 0;
7570 
7571         for (p = 0; p < numPoints; p++) {
7572           PetscInt rStart = newPointOffsets[0][p];
7573           PetscInt b      = points[2 * p];
7574           PetscInt c, r, k;
7575           PetscInt dof;
7576 
7577           PetscCall(PetscSectionGetDof(section, b, &dof));
7578           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7579             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7580             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7581 
7582             for (r = 0; r < nRows; r++) {
7583               for (c = 0; c < newNumIndices; c++) {
7584                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7585               }
7586             }
7587           } else {
7588             /* copy this row as is */
7589             for (r = 0; r < dof; r++) {
7590               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7591             }
7592           }
7593           oldOff += dof;
7594         }
7595       }
7596 
7597       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7598     } else {
7599       newValues = tmpValues;
7600     }
7601   }
7602 
7603   /* clean up */
7604   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7605   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7606 
7607   if (numFields) {
7608     for (f = 0; f < numFields; f++) {
7609       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7610       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7611       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7612     }
7613   } else {
7614     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7615     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7616     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7617   }
7618   PetscCall(ISRestoreIndices(aIS, &anchors));
7619 
7620   /* output */
7621   if (outPoints) {
7622     *outPoints = newPoints;
7623   } else {
7624     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7625   }
7626   if (outValues) *outValues = newValues;
7627   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7628   PetscFunctionReturn(PETSC_SUCCESS);
7629 }
7630 
7631 /*@C
7632   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7633 
7634   Not collective
7635 
7636   Input Parameters:
7637 + dm         - The `DM`
7638 . section    - The `PetscSection` describing the points (a local section)
7639 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7640 . point      - The point defining the closure
7641 - useClPerm  - Use the closure point permutation if available
7642 
7643   Output Parameters:
7644 + numIndices - The number of dof indices in the closure of point with the input sections
7645 . indices    - The dof indices
7646 . outOffsets - Array to write the field offsets into, or `NULL`
7647 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7648 
7649   Level: advanced
7650 
7651   Notes:
7652   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7653 
7654   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7655   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7656   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7657   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7658   indices (with the above semantics) are implied.
7659 
7660 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7661           `PetscSection`, `DMGetGlobalSection()`
7662 @*/
7663 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7664 {
7665   /* Closure ordering */
7666   PetscSection    clSection;
7667   IS              clPoints;
7668   const PetscInt *clp;
7669   PetscInt       *points;
7670   const PetscInt *clperm = NULL;
7671   /* Dof permutation and sign flips */
7672   const PetscInt    **perms[32] = {NULL};
7673   const PetscScalar **flips[32] = {NULL};
7674   PetscScalar        *valCopy   = NULL;
7675   /* Hanging node constraints */
7676   PetscInt    *pointsC = NULL;
7677   PetscScalar *valuesC = NULL;
7678   PetscInt     NclC, NiC;
7679 
7680   PetscInt *idx;
7681   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7682   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7683 
7684   PetscFunctionBeginHot;
7685   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7686   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7687   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7688   if (numIndices) PetscValidIntPointer(numIndices, 6);
7689   if (indices) PetscValidPointer(indices, 7);
7690   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7691   if (values) PetscValidPointer(values, 9);
7692   PetscCall(PetscSectionGetNumFields(section, &Nf));
7693   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7694   PetscCall(PetscArrayzero(offsets, 32));
7695   /* 1) Get points in closure */
7696   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7697   if (useClPerm) {
7698     PetscInt depth, clsize;
7699     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7700     for (clsize = 0, p = 0; p < Ncl; p++) {
7701       PetscInt dof;
7702       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7703       clsize += dof;
7704     }
7705     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7706   }
7707   /* 2) Get number of indices on these points and field offsets from section */
7708   for (p = 0; p < Ncl * 2; p += 2) {
7709     PetscInt dof, fdof;
7710 
7711     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7712     for (f = 0; f < Nf; ++f) {
7713       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7714       offsets[f + 1] += fdof;
7715     }
7716     Ni += dof;
7717   }
7718   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7719   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7720   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7721   for (f = 0; f < PetscMax(1, Nf); ++f) {
7722     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7723     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7724     /* may need to apply sign changes to the element matrix */
7725     if (values && flips[f]) {
7726       PetscInt foffset = offsets[f];
7727 
7728       for (p = 0; p < Ncl; ++p) {
7729         PetscInt           pnt  = points[2 * p], fdof;
7730         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7731 
7732         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7733         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7734         if (flip) {
7735           PetscInt i, j, k;
7736 
7737           if (!valCopy) {
7738             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7739             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7740             *values = valCopy;
7741           }
7742           for (i = 0; i < fdof; ++i) {
7743             PetscScalar fval = flip[i];
7744 
7745             for (k = 0; k < Ni; ++k) {
7746               valCopy[Ni * (foffset + i) + k] *= fval;
7747               valCopy[Ni * k + (foffset + i)] *= fval;
7748             }
7749           }
7750         }
7751         foffset += fdof;
7752       }
7753     }
7754   }
7755   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7756   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7757   if (NclC) {
7758     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7759     for (f = 0; f < PetscMax(1, Nf); ++f) {
7760       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7761       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7762     }
7763     for (f = 0; f < PetscMax(1, Nf); ++f) {
7764       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7765       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7766     }
7767     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7768     Ncl    = NclC;
7769     Ni     = NiC;
7770     points = pointsC;
7771     if (values) *values = valuesC;
7772   }
7773   /* 5) Calculate indices */
7774   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7775   if (Nf) {
7776     PetscInt  idxOff;
7777     PetscBool useFieldOffsets;
7778 
7779     if (outOffsets) {
7780       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7781     }
7782     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7783     if (useFieldOffsets) {
7784       for (p = 0; p < Ncl; ++p) {
7785         const PetscInt pnt = points[p * 2];
7786 
7787         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7788       }
7789     } else {
7790       for (p = 0; p < Ncl; ++p) {
7791         const PetscInt pnt = points[p * 2];
7792 
7793         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7794         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7795          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7796          * global section. */
7797         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7798       }
7799     }
7800   } else {
7801     PetscInt off = 0, idxOff;
7802 
7803     for (p = 0; p < Ncl; ++p) {
7804       const PetscInt  pnt  = points[p * 2];
7805       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7806 
7807       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7808       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7809        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7810       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7811     }
7812   }
7813   /* 6) Cleanup */
7814   for (f = 0; f < PetscMax(1, Nf); ++f) {
7815     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7816     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7817   }
7818   if (NclC) {
7819     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7820   } else {
7821     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7822   }
7823 
7824   if (numIndices) *numIndices = Ni;
7825   if (indices) *indices = idx;
7826   PetscFunctionReturn(PETSC_SUCCESS);
7827 }
7828 
7829 /*@C
7830   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7831 
7832   Not collective
7833 
7834   Input Parameters:
7835 + dm         - The `DM`
7836 . section    - The `PetscSection` describing the points (a local section)
7837 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7838 . point      - The point defining the closure
7839 - useClPerm  - Use the closure point permutation if available
7840 
7841   Output Parameters:
7842 + numIndices - The number of dof indices in the closure of point with the input sections
7843 . indices    - The dof indices
7844 . outOffsets - Array to write the field offsets into, or `NULL`
7845 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7846 
7847   Level: advanced
7848 
7849   Notes:
7850   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7851 
7852   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7853   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7854   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7855   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7856   indices (with the above semantics) are implied.
7857 
7858 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7859 @*/
7860 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7861 {
7862   PetscFunctionBegin;
7863   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7864   PetscValidPointer(indices, 7);
7865   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7866   PetscFunctionReturn(PETSC_SUCCESS);
7867 }
7868 
7869 /*@C
7870   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7871 
7872   Not collective
7873 
7874   Input Parameters:
7875 + dm - The `DM`
7876 . section - The section describing the layout in `v`, or `NULL` to use the default section
7877 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7878 . A - The matrix
7879 . point - The point in the `DM`
7880 . values - The array of values
7881 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7882 
7883   Level: intermediate
7884 
7885 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7886 @*/
7887 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7888 {
7889   DM_Plex           *mesh = (DM_Plex *)dm->data;
7890   PetscInt          *indices;
7891   PetscInt           numIndices;
7892   const PetscScalar *valuesOrig = values;
7893   PetscErrorCode     ierr;
7894 
7895   PetscFunctionBegin;
7896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7897   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7898   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7899   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7900   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7901   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7902 
7903   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7904 
7905   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7906   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7907   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7908   if (ierr) {
7909     PetscMPIInt rank;
7910 
7911     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7912     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7913     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7914     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7915     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7916     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7917   }
7918   if (mesh->printFEM > 1) {
7919     PetscInt i;
7920     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7921     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7922     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7923   }
7924 
7925   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7926   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7927   PetscFunctionReturn(PETSC_SUCCESS);
7928 }
7929 
7930 /*@C
7931   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7932 
7933   Not collective
7934 
7935   Input Parameters:
7936 + dmRow - The `DM` for the row fields
7937 . sectionRow - The section describing the layout, or `NULL` to use the default section in `dmRow`
7938 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7939 . dmCol - The `DM` for the column fields
7940 . sectionCol - The section describing the layout, or `NULL` to use the default section in `dmCol`
7941 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7942 . A - The matrix
7943 . point - The point in the `DM`
7944 . values - The array of values
7945 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7946 
7947   Level: intermediate
7948 
7949 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7950 @*/
7951 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7952 {
7953   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7954   PetscInt          *indicesRow, *indicesCol;
7955   PetscInt           numIndicesRow, numIndicesCol;
7956   const PetscScalar *valuesOrig = values;
7957   PetscErrorCode     ierr;
7958 
7959   PetscFunctionBegin;
7960   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7961   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7962   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7963   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7964   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7965   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7966   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7967   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7968   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7969   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7970   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7971 
7972   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7973   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7974 
7975   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7976   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7977   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7978   if (ierr) {
7979     PetscMPIInt rank;
7980 
7981     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7982     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7983     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7984     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7985     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7986     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7987   }
7988 
7989   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7990   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7991   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7992   PetscFunctionReturn(PETSC_SUCCESS);
7993 }
7994 
7995 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7996 {
7997   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7998   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7999   PetscInt       *cpoints = NULL;
8000   PetscInt       *findices, *cindices;
8001   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8002   PetscInt        foffsets[32], coffsets[32];
8003   DMPolytopeType  ct;
8004   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8005   PetscErrorCode  ierr;
8006 
8007   PetscFunctionBegin;
8008   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8009   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8010   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8011   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8012   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8013   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8014   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8015   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8016   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8017   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8018   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8019   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8020   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8021   PetscCall(PetscArrayzero(foffsets, 32));
8022   PetscCall(PetscArrayzero(coffsets, 32));
8023   /* Column indices */
8024   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8025   maxFPoints = numCPoints;
8026   /* Compress out points not in the section */
8027   /*   TODO: Squeeze out points with 0 dof as well */
8028   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8029   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8030     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8031       cpoints[q * 2]     = cpoints[p];
8032       cpoints[q * 2 + 1] = cpoints[p + 1];
8033       ++q;
8034     }
8035   }
8036   numCPoints = q;
8037   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8038     PetscInt fdof;
8039 
8040     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8041     if (!dof) continue;
8042     for (f = 0; f < numFields; ++f) {
8043       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8044       coffsets[f + 1] += fdof;
8045     }
8046     numCIndices += dof;
8047   }
8048   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8049   /* Row indices */
8050   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8051   {
8052     DMPlexTransform tr;
8053     DMPolytopeType *rct;
8054     PetscInt       *rsize, *rcone, *rornt, Nt;
8055 
8056     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8057     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8058     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8059     numSubcells = rsize[Nt - 1];
8060     PetscCall(DMPlexTransformDestroy(&tr));
8061   }
8062   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8063   for (r = 0, q = 0; r < numSubcells; ++r) {
8064     /* TODO Map from coarse to fine cells */
8065     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8066     /* Compress out points not in the section */
8067     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8068     for (p = 0; p < numFPoints * 2; p += 2) {
8069       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8070         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8071         if (!dof) continue;
8072         for (s = 0; s < q; ++s)
8073           if (fpoints[p] == ftotpoints[s * 2]) break;
8074         if (s < q) continue;
8075         ftotpoints[q * 2]     = fpoints[p];
8076         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8077         ++q;
8078       }
8079     }
8080     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8081   }
8082   numFPoints = q;
8083   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8084     PetscInt fdof;
8085 
8086     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8087     if (!dof) continue;
8088     for (f = 0; f < numFields; ++f) {
8089       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8090       foffsets[f + 1] += fdof;
8091     }
8092     numFIndices += dof;
8093   }
8094   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8095 
8096   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8097   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8098   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8099   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8100   if (numFields) {
8101     const PetscInt **permsF[32] = {NULL};
8102     const PetscInt **permsC[32] = {NULL};
8103 
8104     for (f = 0; f < numFields; f++) {
8105       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8106       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8107     }
8108     for (p = 0; p < numFPoints; p++) {
8109       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8110       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8111     }
8112     for (p = 0; p < numCPoints; p++) {
8113       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8114       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8115     }
8116     for (f = 0; f < numFields; f++) {
8117       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8118       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8119     }
8120   } else {
8121     const PetscInt **permsF = NULL;
8122     const PetscInt **permsC = NULL;
8123 
8124     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8125     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8126     for (p = 0, off = 0; p < numFPoints; p++) {
8127       const PetscInt *perm = permsF ? permsF[p] : NULL;
8128 
8129       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8130       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8131     }
8132     for (p = 0, off = 0; p < numCPoints; p++) {
8133       const PetscInt *perm = permsC ? permsC[p] : NULL;
8134 
8135       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8136       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8137     }
8138     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8139     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8140   }
8141   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8142   /* TODO: flips */
8143   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8144   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8145   if (ierr) {
8146     PetscMPIInt rank;
8147 
8148     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8149     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8150     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8151     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8152     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8153   }
8154   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8155   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8156   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8157   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8158   PetscFunctionReturn(PETSC_SUCCESS);
8159 }
8160 
8161 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8162 {
8163   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8164   PetscInt       *cpoints = NULL;
8165   PetscInt        foffsets[32], coffsets[32];
8166   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8167   DMPolytopeType  ct;
8168   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8169 
8170   PetscFunctionBegin;
8171   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8172   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8173   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8174   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8175   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8176   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8177   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8178   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8179   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8180   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8181   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8182   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8183   PetscCall(PetscArrayzero(foffsets, 32));
8184   PetscCall(PetscArrayzero(coffsets, 32));
8185   /* Column indices */
8186   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8187   maxFPoints = numCPoints;
8188   /* Compress out points not in the section */
8189   /*   TODO: Squeeze out points with 0 dof as well */
8190   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8191   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8192     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8193       cpoints[q * 2]     = cpoints[p];
8194       cpoints[q * 2 + 1] = cpoints[p + 1];
8195       ++q;
8196     }
8197   }
8198   numCPoints = q;
8199   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8200     PetscInt fdof;
8201 
8202     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8203     if (!dof) continue;
8204     for (f = 0; f < numFields; ++f) {
8205       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8206       coffsets[f + 1] += fdof;
8207     }
8208     numCIndices += dof;
8209   }
8210   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8211   /* Row indices */
8212   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8213   {
8214     DMPlexTransform tr;
8215     DMPolytopeType *rct;
8216     PetscInt       *rsize, *rcone, *rornt, Nt;
8217 
8218     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8219     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8220     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8221     numSubcells = rsize[Nt - 1];
8222     PetscCall(DMPlexTransformDestroy(&tr));
8223   }
8224   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8225   for (r = 0, q = 0; r < numSubcells; ++r) {
8226     /* TODO Map from coarse to fine cells */
8227     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8228     /* Compress out points not in the section */
8229     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8230     for (p = 0; p < numFPoints * 2; p += 2) {
8231       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8232         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8233         if (!dof) continue;
8234         for (s = 0; s < q; ++s)
8235           if (fpoints[p] == ftotpoints[s * 2]) break;
8236         if (s < q) continue;
8237         ftotpoints[q * 2]     = fpoints[p];
8238         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8239         ++q;
8240       }
8241     }
8242     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8243   }
8244   numFPoints = q;
8245   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8246     PetscInt fdof;
8247 
8248     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8249     if (!dof) continue;
8250     for (f = 0; f < numFields; ++f) {
8251       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8252       foffsets[f + 1] += fdof;
8253     }
8254     numFIndices += dof;
8255   }
8256   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8257 
8258   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8259   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8260   if (numFields) {
8261     const PetscInt **permsF[32] = {NULL};
8262     const PetscInt **permsC[32] = {NULL};
8263 
8264     for (f = 0; f < numFields; f++) {
8265       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8266       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8267     }
8268     for (p = 0; p < numFPoints; p++) {
8269       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8270       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8271     }
8272     for (p = 0; p < numCPoints; p++) {
8273       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8274       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8275     }
8276     for (f = 0; f < numFields; f++) {
8277       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8278       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8279     }
8280   } else {
8281     const PetscInt **permsF = NULL;
8282     const PetscInt **permsC = NULL;
8283 
8284     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8285     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8286     for (p = 0, off = 0; p < numFPoints; p++) {
8287       const PetscInt *perm = permsF ? permsF[p] : NULL;
8288 
8289       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8290       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8291     }
8292     for (p = 0, off = 0; p < numCPoints; p++) {
8293       const PetscInt *perm = permsC ? permsC[p] : NULL;
8294 
8295       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8296       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8297     }
8298     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8299     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8300   }
8301   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8302   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8303   PetscFunctionReturn(PETSC_SUCCESS);
8304 }
8305 
8306 /*@C
8307   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8308 
8309   Input Parameter:
8310 . dm   - The `DMPLEX` object
8311 
8312   Output Parameter:
8313 . cellHeight - The height of a cell
8314 
8315   Level: developer
8316 
8317 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8318 @*/
8319 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8320 {
8321   DM_Plex *mesh = (DM_Plex *)dm->data;
8322 
8323   PetscFunctionBegin;
8324   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8325   PetscValidIntPointer(cellHeight, 2);
8326   *cellHeight = mesh->vtkCellHeight;
8327   PetscFunctionReturn(PETSC_SUCCESS);
8328 }
8329 
8330 /*@C
8331   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8332 
8333   Input Parameters:
8334 + dm   - The `DMPLEX` object
8335 - cellHeight - The height of a cell
8336 
8337   Level: developer
8338 
8339 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8340 @*/
8341 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8342 {
8343   DM_Plex *mesh = (DM_Plex *)dm->data;
8344 
8345   PetscFunctionBegin;
8346   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8347   mesh->vtkCellHeight = cellHeight;
8348   PetscFunctionReturn(PETSC_SUCCESS);
8349 }
8350 
8351 /*@
8352   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8353 
8354   Input Parameters:
8355 + dm - The `DMPLEX` object
8356 - ct - The `DMPolytopeType` of the cell
8357 
8358   Output Parameters:
8359 + start - The first cell of this type, or `NULL`
8360 - end   - The upper bound on this celltype, or `NULL`
8361 
8362   Level: advanced
8363 
8364 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8365 @*/
8366 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8367 {
8368   DM_Plex *mesh = (DM_Plex *)dm->data;
8369   DMLabel  label;
8370   PetscInt pStart, pEnd;
8371 
8372   PetscFunctionBegin;
8373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8374   if (start) {
8375     PetscValidIntPointer(start, 3);
8376     *start = 0;
8377   }
8378   if (end) {
8379     PetscValidIntPointer(end, 4);
8380     *end = 0;
8381   }
8382   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8383   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8384   if (mesh->tr) {
8385     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8386   } else {
8387     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8388     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8389     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8390   }
8391   PetscFunctionReturn(PETSC_SUCCESS);
8392 }
8393 
8394 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8395 {
8396   PetscSection section, globalSection;
8397   PetscInt    *numbers, p;
8398 
8399   PetscFunctionBegin;
8400   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8401   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8402   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8403   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8404   PetscCall(PetscSectionSetUp(section));
8405   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8406   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8407   for (p = pStart; p < pEnd; ++p) {
8408     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8409     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8410     else numbers[p - pStart] += shift;
8411   }
8412   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8413   if (globalSize) {
8414     PetscLayout layout;
8415     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8416     PetscCall(PetscLayoutGetSize(layout, globalSize));
8417     PetscCall(PetscLayoutDestroy(&layout));
8418   }
8419   PetscCall(PetscSectionDestroy(&section));
8420   PetscCall(PetscSectionDestroy(&globalSection));
8421   PetscFunctionReturn(PETSC_SUCCESS);
8422 }
8423 
8424 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8425 {
8426   PetscInt cellHeight, cStart, cEnd;
8427 
8428   PetscFunctionBegin;
8429   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8430   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8431   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8432   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8433   PetscFunctionReturn(PETSC_SUCCESS);
8434 }
8435 
8436 /*@
8437   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8438 
8439   Input Parameter:
8440 . dm   - The `DMPLEX` object
8441 
8442   Output Parameter:
8443 . globalCellNumbers - Global cell numbers for all cells on this process
8444 
8445   Level: developer
8446 
8447 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8448 @*/
8449 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8450 {
8451   DM_Plex *mesh = (DM_Plex *)dm->data;
8452 
8453   PetscFunctionBegin;
8454   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8455   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8456   *globalCellNumbers = mesh->globalCellNumbers;
8457   PetscFunctionReturn(PETSC_SUCCESS);
8458 }
8459 
8460 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8461 {
8462   PetscInt vStart, vEnd;
8463 
8464   PetscFunctionBegin;
8465   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8466   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8467   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8468   PetscFunctionReturn(PETSC_SUCCESS);
8469 }
8470 
8471 /*@
8472   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8473 
8474   Input Parameter:
8475 . dm   - The `DMPLEX` object
8476 
8477   Output Parameter:
8478 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8479 
8480   Level: developer
8481 
8482 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8483 @*/
8484 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8485 {
8486   DM_Plex *mesh = (DM_Plex *)dm->data;
8487 
8488   PetscFunctionBegin;
8489   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8490   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8491   *globalVertexNumbers = mesh->globalVertexNumbers;
8492   PetscFunctionReturn(PETSC_SUCCESS);
8493 }
8494 
8495 /*@
8496   DMPlexCreatePointNumbering - Create a global numbering for all points.
8497 
8498   Collective
8499 
8500   Input Parameter:
8501 . dm   - The `DMPLEX` object
8502 
8503   Output Parameter:
8504 . globalPointNumbers - Global numbers for all points on this process
8505 
8506   Level: developer
8507 
8508   Notes:
8509   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8510   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8511   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8512   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8513 
8514   The partitioned mesh is
8515 ```
8516  (2)--0--(3)--1--(4)    (1)--0--(2)
8517 ```
8518   and its global numbering is
8519 ```
8520   (3)--0--(4)--1--(5)--2--(6)
8521 ```
8522   Then the global numbering is provided as
8523 ```
8524 [0] Number of indices in set 5
8525 [0] 0 0
8526 [0] 1 1
8527 [0] 2 3
8528 [0] 3 4
8529 [0] 4 -6
8530 [1] Number of indices in set 3
8531 [1] 0 2
8532 [1] 1 5
8533 [1] 2 6
8534 ```
8535 
8536 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8537 @*/
8538 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8539 {
8540   IS        nums[4];
8541   PetscInt  depths[4], gdepths[4], starts[4];
8542   PetscInt  depth, d, shift = 0;
8543   PetscBool empty = PETSC_FALSE;
8544 
8545   PetscFunctionBegin;
8546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8547   PetscCall(DMPlexGetDepth(dm, &depth));
8548   // For unstratified meshes use dim instead of depth
8549   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8550   // If any stratum is empty, we must mark all empty
8551   for (d = 0; d <= depth; ++d) {
8552     PetscInt end;
8553 
8554     depths[d] = depth - d;
8555     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8556     if (!(starts[d] - end)) empty = PETSC_TRUE;
8557   }
8558   if (empty)
8559     for (d = 0; d <= depth; ++d) {
8560       depths[d] = -1;
8561       starts[d] = -1;
8562     }
8563   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8564   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8565   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]);
8566   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8567   for (d = 0; d <= depth; ++d) {
8568     PetscInt pStart, pEnd, gsize;
8569 
8570     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8571     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8572     shift += gsize;
8573   }
8574   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8575   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8576   PetscFunctionReturn(PETSC_SUCCESS);
8577 }
8578 
8579 /*@
8580   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8581 
8582   Input Parameter:
8583 . dm - The `DMPLEX` object
8584 
8585   Output Parameter:
8586 . ranks - The rank field
8587 
8588   Options Database Key:
8589 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8590 
8591   Level: intermediate
8592 
8593 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8594 @*/
8595 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8596 {
8597   DM             rdm;
8598   PetscFE        fe;
8599   PetscScalar   *r;
8600   PetscMPIInt    rank;
8601   DMPolytopeType ct;
8602   PetscInt       dim, cStart, cEnd, c;
8603   PetscBool      simplex;
8604 
8605   PetscFunctionBeginUser;
8606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8607   PetscValidPointer(ranks, 2);
8608   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8609   PetscCall(DMClone(dm, &rdm));
8610   PetscCall(DMGetDimension(rdm, &dim));
8611   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8612   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8613   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8614   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8615   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8616   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8617   PetscCall(PetscFEDestroy(&fe));
8618   PetscCall(DMCreateDS(rdm));
8619   PetscCall(DMCreateGlobalVector(rdm, ranks));
8620   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8621   PetscCall(VecGetArray(*ranks, &r));
8622   for (c = cStart; c < cEnd; ++c) {
8623     PetscScalar *lr;
8624 
8625     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8626     if (lr) *lr = rank;
8627   }
8628   PetscCall(VecRestoreArray(*ranks, &r));
8629   PetscCall(DMDestroy(&rdm));
8630   PetscFunctionReturn(PETSC_SUCCESS);
8631 }
8632 
8633 /*@
8634   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8635 
8636   Input Parameters:
8637 + dm    - The `DMPLEX`
8638 - label - The `DMLabel`
8639 
8640   Output Parameter:
8641 . val - The label value field
8642 
8643   Options Database Key:
8644 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8645 
8646   Level: intermediate
8647 
8648 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8649 @*/
8650 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8651 {
8652   DM           rdm;
8653   PetscFE      fe;
8654   PetscScalar *v;
8655   PetscInt     dim, cStart, cEnd, c;
8656 
8657   PetscFunctionBeginUser;
8658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8659   PetscValidPointer(label, 2);
8660   PetscValidPointer(val, 3);
8661   PetscCall(DMClone(dm, &rdm));
8662   PetscCall(DMGetDimension(rdm, &dim));
8663   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8664   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8665   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8666   PetscCall(PetscFEDestroy(&fe));
8667   PetscCall(DMCreateDS(rdm));
8668   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8669   PetscCall(DMCreateGlobalVector(rdm, val));
8670   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8671   PetscCall(VecGetArray(*val, &v));
8672   for (c = cStart; c < cEnd; ++c) {
8673     PetscScalar *lv;
8674     PetscInt     cval;
8675 
8676     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8677     PetscCall(DMLabelGetValue(label, c, &cval));
8678     *lv = cval;
8679   }
8680   PetscCall(VecRestoreArray(*val, &v));
8681   PetscCall(DMDestroy(&rdm));
8682   PetscFunctionReturn(PETSC_SUCCESS);
8683 }
8684 
8685 /*@
8686   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8687 
8688   Input Parameter:
8689 . dm - The `DMPLEX` object
8690 
8691   Level: developer
8692 
8693   Notes:
8694   This is a useful diagnostic when creating meshes programmatically.
8695 
8696   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8697 
8698 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8699 @*/
8700 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8701 {
8702   PetscSection    coneSection, supportSection;
8703   const PetscInt *cone, *support;
8704   PetscInt        coneSize, c, supportSize, s;
8705   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8706   PetscBool       storagecheck = PETSC_TRUE;
8707 
8708   PetscFunctionBegin;
8709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8710   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8711   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8712   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8713   /* Check that point p is found in the support of its cone points, and vice versa */
8714   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8715   for (p = pStart; p < pEnd; ++p) {
8716     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8717     PetscCall(DMPlexGetCone(dm, p, &cone));
8718     for (c = 0; c < coneSize; ++c) {
8719       PetscBool dup = PETSC_FALSE;
8720       PetscInt  d;
8721       for (d = c - 1; d >= 0; --d) {
8722         if (cone[c] == cone[d]) {
8723           dup = PETSC_TRUE;
8724           break;
8725         }
8726       }
8727       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8728       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8729       for (s = 0; s < supportSize; ++s) {
8730         if (support[s] == p) break;
8731       }
8732       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8733         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8734         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8735         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8736         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8737         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8738         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8739         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]);
8740         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8741       }
8742     }
8743     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8744     if (p != pp) {
8745       storagecheck = PETSC_FALSE;
8746       continue;
8747     }
8748     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8749     PetscCall(DMPlexGetSupport(dm, p, &support));
8750     for (s = 0; s < supportSize; ++s) {
8751       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8752       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8753       for (c = 0; c < coneSize; ++c) {
8754         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8755         if (cone[c] != pp) {
8756           c = 0;
8757           break;
8758         }
8759         if (cone[c] == p) break;
8760       }
8761       if (c >= coneSize) {
8762         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8763         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8764         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8765         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8766         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8767         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8768         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8769       }
8770     }
8771   }
8772   if (storagecheck) {
8773     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8774     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8775     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8776   }
8777   PetscFunctionReturn(PETSC_SUCCESS);
8778 }
8779 
8780 /*
8781   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.
8782 */
8783 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8784 {
8785   DMPolytopeType  cct;
8786   PetscInt        ptpoints[4];
8787   const PetscInt *cone, *ccone, *ptcone;
8788   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8789 
8790   PetscFunctionBegin;
8791   *unsplit = 0;
8792   switch (ct) {
8793   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8794     ptpoints[npt++] = c;
8795     break;
8796   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8797     PetscCall(DMPlexGetCone(dm, c, &cone));
8798     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8799     for (cp = 0; cp < coneSize; ++cp) {
8800       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8801       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8802     }
8803     break;
8804   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8805   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8806     PetscCall(DMPlexGetCone(dm, c, &cone));
8807     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8808     for (cp = 0; cp < coneSize; ++cp) {
8809       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8810       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8811       for (ccp = 0; ccp < cconeSize; ++ccp) {
8812         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8813         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8814           PetscInt p;
8815           for (p = 0; p < npt; ++p)
8816             if (ptpoints[p] == ccone[ccp]) break;
8817           if (p == npt) ptpoints[npt++] = ccone[ccp];
8818         }
8819       }
8820     }
8821     break;
8822   default:
8823     break;
8824   }
8825   for (pt = 0; pt < npt; ++pt) {
8826     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8827     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8828   }
8829   PetscFunctionReturn(PETSC_SUCCESS);
8830 }
8831 
8832 /*@
8833   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8834 
8835   Input Parameters:
8836 + dm - The `DMPLEX` object
8837 - cellHeight - Normally 0
8838 
8839   Level: developer
8840 
8841   Notes:
8842   This is a useful diagnostic when creating meshes programmatically.
8843   Currently applicable only to homogeneous simplex or tensor meshes.
8844 
8845   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8846 
8847 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8848 @*/
8849 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8850 {
8851   DMPlexInterpolatedFlag interp;
8852   DMPolytopeType         ct;
8853   PetscInt               vStart, vEnd, cStart, cEnd, c;
8854 
8855   PetscFunctionBegin;
8856   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8857   PetscCall(DMPlexIsInterpolated(dm, &interp));
8858   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8859   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8860   for (c = cStart; c < cEnd; ++c) {
8861     PetscInt *closure = NULL;
8862     PetscInt  coneSize, closureSize, cl, Nv = 0;
8863 
8864     PetscCall(DMPlexGetCellType(dm, c, &ct));
8865     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8866     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8867     if (interp == DMPLEX_INTERPOLATED_FULL) {
8868       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8869       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));
8870     }
8871     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8872     for (cl = 0; cl < closureSize * 2; cl += 2) {
8873       const PetscInt p = closure[cl];
8874       if ((p >= vStart) && (p < vEnd)) ++Nv;
8875     }
8876     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8877     /* Special Case: Tensor faces with identified vertices */
8878     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8879       PetscInt unsplit;
8880 
8881       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8882       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8883     }
8884     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));
8885   }
8886   PetscFunctionReturn(PETSC_SUCCESS);
8887 }
8888 
8889 /*@
8890   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8891 
8892   Collective
8893 
8894   Input Parameters:
8895 + dm - The `DMPLEX` object
8896 - cellHeight - Normally 0
8897 
8898   Level: developer
8899 
8900   Notes:
8901   This is a useful diagnostic when creating meshes programmatically.
8902   This routine is only relevant for meshes that are fully interpolated across all ranks.
8903   It will error out if a partially interpolated mesh is given on some rank.
8904   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8905 
8906   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8907 
8908 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8909 @*/
8910 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8911 {
8912   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8913   DMPlexInterpolatedFlag interpEnum;
8914 
8915   PetscFunctionBegin;
8916   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8917   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8918   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8919   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8920     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8921     PetscFunctionReturn(PETSC_SUCCESS);
8922   }
8923 
8924   PetscCall(DMGetDimension(dm, &dim));
8925   PetscCall(DMPlexGetDepth(dm, &depth));
8926   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8927   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8928     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8929     for (c = cStart; c < cEnd; ++c) {
8930       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8931       const DMPolytopeType *faceTypes;
8932       DMPolytopeType        ct;
8933       PetscInt              numFaces, coneSize, f;
8934       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8935 
8936       PetscCall(DMPlexGetCellType(dm, c, &ct));
8937       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8938       if (unsplit) continue;
8939       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8940       PetscCall(DMPlexGetCone(dm, c, &cone));
8941       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8942       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8943       for (cl = 0; cl < closureSize * 2; cl += 2) {
8944         const PetscInt p = closure[cl];
8945         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8946       }
8947       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8948       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);
8949       for (f = 0; f < numFaces; ++f) {
8950         DMPolytopeType fct;
8951         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8952 
8953         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8954         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8955         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8956           const PetscInt p = fclosure[cl];
8957           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8958         }
8959         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]);
8960         for (v = 0; v < fnumCorners; ++v) {
8961           if (fclosure[v] != faces[fOff + v]) {
8962             PetscInt v1;
8963 
8964             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8965             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8966             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8967             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8968             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8969             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]);
8970           }
8971         }
8972         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8973         fOff += faceSizes[f];
8974       }
8975       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8976       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8977     }
8978   }
8979   PetscFunctionReturn(PETSC_SUCCESS);
8980 }
8981 
8982 /*@
8983   DMPlexCheckGeometry - Check the geometry of mesh cells
8984 
8985   Input Parameter:
8986 . dm - The `DMPLEX` object
8987 
8988   Level: developer
8989 
8990   Notes:
8991   This is a useful diagnostic when creating meshes programmatically.
8992 
8993   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8994 
8995 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8996 @*/
8997 PetscErrorCode DMPlexCheckGeometry(DM dm)
8998 {
8999   Vec       coordinates;
9000   PetscReal detJ, J[9], refVol = 1.0;
9001   PetscReal vol;
9002   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9003 
9004   PetscFunctionBegin;
9005   PetscCall(DMGetDimension(dm, &dim));
9006   PetscCall(DMGetCoordinateDim(dm, &dE));
9007   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9008   PetscCall(DMPlexGetDepth(dm, &depth));
9009   for (d = 0; d < dim; ++d) refVol *= 2.0;
9010   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9011   /* Make sure local coordinates are created, because that step is collective */
9012   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9013   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9014   for (c = cStart; c < cEnd; ++c) {
9015     DMPolytopeType ct;
9016     PetscInt       unsplit;
9017     PetscBool      ignoreZeroVol = PETSC_FALSE;
9018 
9019     PetscCall(DMPlexGetCellType(dm, c, &ct));
9020     switch (ct) {
9021     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9022     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9023     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9024       ignoreZeroVol = PETSC_TRUE;
9025       break;
9026     default:
9027       break;
9028     }
9029     switch (ct) {
9030     case DM_POLYTOPE_TRI_PRISM:
9031     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9032     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9033     case DM_POLYTOPE_PYRAMID:
9034       continue;
9035     default:
9036       break;
9037     }
9038     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9039     if (unsplit) continue;
9040     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9041     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);
9042     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9043     /* This should work with periodicity since DG coordinates should be used */
9044     if (depth > 1) {
9045       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9046       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);
9047       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9048     }
9049   }
9050   PetscFunctionReturn(PETSC_SUCCESS);
9051 }
9052 
9053 /*@
9054   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9055 
9056   Collective
9057 
9058   Input Parameters:
9059 + dm - The `DMPLEX` object
9060 . pointSF - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9061 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9062 
9063   Level: developer
9064 
9065   Notes:
9066   This is mainly intended for debugging/testing purposes.
9067 
9068   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9069 
9070   Extra roots can come from priodic cuts, where additional points appear on the boundary
9071 
9072 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9073 @*/
9074 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9075 {
9076   PetscInt           l, nleaves, nroots, overlap;
9077   const PetscInt    *locals;
9078   const PetscSFNode *remotes;
9079   PetscBool          distributed;
9080   MPI_Comm           comm;
9081   PetscMPIInt        rank;
9082 
9083   PetscFunctionBegin;
9084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9085   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9086   else pointSF = dm->sf;
9087   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9088   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9089   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9090   {
9091     PetscMPIInt mpiFlag;
9092 
9093     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9094     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9095   }
9096   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9097   PetscCall(DMPlexIsDistributed(dm, &distributed));
9098   if (!distributed) {
9099     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);
9100     PetscFunctionReturn(PETSC_SUCCESS);
9101   }
9102   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);
9103   PetscCall(DMPlexGetOverlap(dm, &overlap));
9104 
9105   /* Check SF graph is compatible with DMPlex chart */
9106   {
9107     PetscInt pStart, pEnd, maxLeaf;
9108 
9109     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9110     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9111     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9112     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9113   }
9114 
9115   /* Check Point SF has no local points referenced */
9116   for (l = 0; l < nleaves; l++) {
9117     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);
9118   }
9119 
9120   /* Check there are no cells in interface */
9121   if (!overlap) {
9122     PetscInt cellHeight, cStart, cEnd;
9123 
9124     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9125     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9126     for (l = 0; l < nleaves; ++l) {
9127       const PetscInt point = locals ? locals[l] : l;
9128 
9129       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9130     }
9131   }
9132 
9133   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9134   {
9135     const PetscInt *rootdegree;
9136 
9137     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9138     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9139     for (l = 0; l < nleaves; ++l) {
9140       const PetscInt  point = locals ? locals[l] : l;
9141       const PetscInt *cone;
9142       PetscInt        coneSize, c, idx;
9143 
9144       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9145       PetscCall(DMPlexGetCone(dm, point, &cone));
9146       for (c = 0; c < coneSize; ++c) {
9147         if (!rootdegree[cone[c]]) {
9148           if (locals) {
9149             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9150           } else {
9151             idx = (cone[c] < nleaves) ? cone[c] : -1;
9152           }
9153           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9154         }
9155       }
9156     }
9157   }
9158   PetscFunctionReturn(PETSC_SUCCESS);
9159 }
9160 
9161 /*@
9162   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9163 
9164   Input Parameter:
9165 . dm - The `DMPLEX` object
9166 
9167   Level: developer
9168 
9169   Notes:
9170   This is a useful diagnostic when creating meshes programmatically.
9171 
9172   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9173 
9174   Currently does not include `DMPlexCheckCellShape()`.
9175 
9176 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9177 @*/
9178 PetscErrorCode DMPlexCheck(DM dm)
9179 {
9180   PetscInt cellHeight;
9181 
9182   PetscFunctionBegin;
9183   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9184   PetscCall(DMPlexCheckSymmetry(dm));
9185   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9186   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9187   PetscCall(DMPlexCheckGeometry(dm));
9188   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9189   PetscCall(DMPlexCheckInterfaceCones(dm));
9190   PetscFunctionReturn(PETSC_SUCCESS);
9191 }
9192 
9193 typedef struct cell_stats {
9194   PetscReal min, max, sum, squaresum;
9195   PetscInt  count;
9196 } cell_stats_t;
9197 
9198 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9199 {
9200   PetscInt i, N = *len;
9201 
9202   for (i = 0; i < N; i++) {
9203     cell_stats_t *A = (cell_stats_t *)a;
9204     cell_stats_t *B = (cell_stats_t *)b;
9205 
9206     B->min = PetscMin(A->min, B->min);
9207     B->max = PetscMax(A->max, B->max);
9208     B->sum += A->sum;
9209     B->squaresum += A->squaresum;
9210     B->count += A->count;
9211   }
9212 }
9213 
9214 /*@
9215   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9216 
9217   Collective
9218 
9219   Input Parameters:
9220 + dm        - The `DMPLEX` object
9221 . output    - If true, statistics will be displayed on `stdout`
9222 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9223 
9224   Level: developer
9225 
9226   Notes:
9227   This is mainly intended for debugging/testing purposes.
9228 
9229   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9230 
9231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9232 @*/
9233 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9234 {
9235   DM           dmCoarse;
9236   cell_stats_t stats, globalStats;
9237   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9238   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9239   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9240   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9241   PetscMPIInt  rank, size;
9242 
9243   PetscFunctionBegin;
9244   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9245   stats.min = PETSC_MAX_REAL;
9246   stats.max = PETSC_MIN_REAL;
9247   stats.sum = stats.squaresum = 0.;
9248   stats.count                 = 0;
9249 
9250   PetscCallMPI(MPI_Comm_size(comm, &size));
9251   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9252   PetscCall(DMGetCoordinateDim(dm, &cdim));
9253   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9254   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9255   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9256   for (c = cStart; c < cEnd; c++) {
9257     PetscInt  i;
9258     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9259 
9260     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9261     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9262     for (i = 0; i < PetscSqr(cdim); ++i) {
9263       frobJ += J[i] * J[i];
9264       frobInvJ += invJ[i] * invJ[i];
9265     }
9266     cond2 = frobJ * frobInvJ;
9267     cond  = PetscSqrtReal(cond2);
9268 
9269     stats.min = PetscMin(stats.min, cond);
9270     stats.max = PetscMax(stats.max, cond);
9271     stats.sum += cond;
9272     stats.squaresum += cond2;
9273     stats.count++;
9274     if (output && cond > limit) {
9275       PetscSection coordSection;
9276       Vec          coordsLocal;
9277       PetscScalar *coords = NULL;
9278       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9279 
9280       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9281       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9282       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9283       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9284       for (i = 0; i < Nv / cdim; ++i) {
9285         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9286         for (d = 0; d < cdim; ++d) {
9287           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9288           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9289         }
9290         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9291       }
9292       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9293       for (cl = 0; cl < clSize * 2; cl += 2) {
9294         const PetscInt edge = closure[cl];
9295 
9296         if ((edge >= eStart) && (edge < eEnd)) {
9297           PetscReal len;
9298 
9299           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9300           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9301         }
9302       }
9303       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9304       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9305     }
9306   }
9307   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9308 
9309   if (size > 1) {
9310     PetscMPIInt  blockLengths[2] = {4, 1};
9311     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9312     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9313     MPI_Op       statReduce;
9314 
9315     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9316     PetscCallMPI(MPI_Type_commit(&statType));
9317     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9318     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9319     PetscCallMPI(MPI_Op_free(&statReduce));
9320     PetscCallMPI(MPI_Type_free(&statType));
9321   } else {
9322     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9323   }
9324   if (rank == 0) {
9325     count = globalStats.count;
9326     min   = globalStats.min;
9327     max   = globalStats.max;
9328     mean  = globalStats.sum / globalStats.count;
9329     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9330   }
9331 
9332   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));
9333   PetscCall(PetscFree2(J, invJ));
9334 
9335   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9336   if (dmCoarse) {
9337     PetscBool isplex;
9338 
9339     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9340     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9341   }
9342   PetscFunctionReturn(PETSC_SUCCESS);
9343 }
9344 
9345 /*@
9346   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9347   orthogonal quality below given tolerance.
9348 
9349   Collective
9350 
9351   Input Parameters:
9352 + dm   - The `DMPLEX` object
9353 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9354 - atol - [0, 1] Absolute tolerance for tagging cells.
9355 
9356   Output Parameters:
9357 + OrthQual      - `Vec` containing orthogonal quality per cell
9358 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9359 
9360   Options Database Keys:
9361 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9362 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9363 
9364   Level: intermediate
9365 
9366   Notes:
9367   Orthogonal quality is given by the following formula:
9368 
9369   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9370 
9371   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
9372   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9373   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9374   calculating the cosine of the angle between these vectors.
9375 
9376   Orthogonal quality ranges from 1 (best) to 0 (worst).
9377 
9378   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9379   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9380 
9381   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9382 
9383 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9384 @*/
9385 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9386 {
9387   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9388   PetscInt              *idx;
9389   PetscScalar           *oqVals;
9390   const PetscScalar     *cellGeomArr, *faceGeomArr;
9391   PetscReal             *ci, *fi, *Ai;
9392   MPI_Comm               comm;
9393   Vec                    cellgeom, facegeom;
9394   DM                     dmFace, dmCell;
9395   IS                     glob;
9396   ISLocalToGlobalMapping ltog;
9397   PetscViewer            vwr;
9398 
9399   PetscFunctionBegin;
9400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9401   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9402   PetscValidPointer(OrthQual, 4);
9403   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9404   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9405   PetscCall(DMGetDimension(dm, &nc));
9406   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9407   {
9408     DMPlexInterpolatedFlag interpFlag;
9409 
9410     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9411     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9412       PetscMPIInt rank;
9413 
9414       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9415       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9416     }
9417   }
9418   if (OrthQualLabel) {
9419     PetscValidPointer(OrthQualLabel, 5);
9420     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9421     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9422   } else {
9423     *OrthQualLabel = NULL;
9424   }
9425   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9426   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9427   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9428   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9429   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9430   PetscCall(VecCreate(comm, OrthQual));
9431   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9432   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9433   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9434   PetscCall(VecSetUp(*OrthQual));
9435   PetscCall(ISDestroy(&glob));
9436   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9437   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9438   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9439   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9440   PetscCall(VecGetDM(cellgeom, &dmCell));
9441   PetscCall(VecGetDM(facegeom, &dmFace));
9442   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9443   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9444     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9445     PetscInt         cellarr[2], *adj = NULL;
9446     PetscScalar     *cArr, *fArr;
9447     PetscReal        minvalc = 1.0, minvalf = 1.0;
9448     PetscFVCellGeom *cg;
9449 
9450     idx[cellIter] = cell - cStart;
9451     cellarr[0]    = cell;
9452     /* Make indexing into cellGeom easier */
9453     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9454     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9455     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9456     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9457     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9458       PetscInt         i;
9459       const PetscInt   neigh  = adj[cellneigh];
9460       PetscReal        normci = 0, normfi = 0, normai = 0;
9461       PetscFVCellGeom *cgneigh;
9462       PetscFVFaceGeom *fg;
9463 
9464       /* Don't count ourselves in the neighbor list */
9465       if (neigh == cell) continue;
9466       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9467       cellarr[1] = neigh;
9468       {
9469         PetscInt        numcovpts;
9470         const PetscInt *covpts;
9471 
9472         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9473         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9474         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9475       }
9476 
9477       /* Compute c_i, f_i and their norms */
9478       for (i = 0; i < nc; i++) {
9479         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9480         fi[i] = fg->centroid[i] - cg->centroid[i];
9481         Ai[i] = fg->normal[i];
9482         normci += PetscPowReal(ci[i], 2);
9483         normfi += PetscPowReal(fi[i], 2);
9484         normai += PetscPowReal(Ai[i], 2);
9485       }
9486       normci = PetscSqrtReal(normci);
9487       normfi = PetscSqrtReal(normfi);
9488       normai = PetscSqrtReal(normai);
9489 
9490       /* Normalize and compute for each face-cell-normal pair */
9491       for (i = 0; i < nc; i++) {
9492         ci[i] = ci[i] / normci;
9493         fi[i] = fi[i] / normfi;
9494         Ai[i] = Ai[i] / normai;
9495         /* PetscAbs because I don't know if normals are guaranteed to point out */
9496         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9497         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9498       }
9499       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9500       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9501     }
9502     PetscCall(PetscFree(adj));
9503     PetscCall(PetscFree2(cArr, fArr));
9504     /* Defer to cell if they're equal */
9505     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9506     if (OrthQualLabel) {
9507       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9508     }
9509   }
9510   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9511   PetscCall(VecAssemblyBegin(*OrthQual));
9512   PetscCall(VecAssemblyEnd(*OrthQual));
9513   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9514   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9515   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9516   if (OrthQualLabel) {
9517     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9518   }
9519   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9520   PetscCall(PetscViewerDestroy(&vwr));
9521   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9522   PetscFunctionReturn(PETSC_SUCCESS);
9523 }
9524 
9525 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9526  * interpolator construction */
9527 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9528 {
9529   PetscSection section, newSection, gsection;
9530   PetscSF      sf;
9531   PetscBool    hasConstraints, ghasConstraints;
9532 
9533   PetscFunctionBegin;
9534   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9535   PetscValidPointer(odm, 2);
9536   PetscCall(DMGetLocalSection(dm, &section));
9537   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9538   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9539   if (!ghasConstraints) {
9540     PetscCall(PetscObjectReference((PetscObject)dm));
9541     *odm = dm;
9542     PetscFunctionReturn(PETSC_SUCCESS);
9543   }
9544   PetscCall(DMClone(dm, odm));
9545   PetscCall(DMCopyFields(dm, *odm));
9546   PetscCall(DMGetLocalSection(*odm, &newSection));
9547   PetscCall(DMGetPointSF(*odm, &sf));
9548   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9549   PetscCall(DMSetGlobalSection(*odm, gsection));
9550   PetscCall(PetscSectionDestroy(&gsection));
9551   PetscFunctionReturn(PETSC_SUCCESS);
9552 }
9553 
9554 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9555 {
9556   DM        dmco, dmfo;
9557   Mat       interpo;
9558   Vec       rscale;
9559   Vec       cglobalo, clocal;
9560   Vec       fglobal, fglobalo, flocal;
9561   PetscBool regular;
9562 
9563   PetscFunctionBegin;
9564   PetscCall(DMGetFullDM(dmc, &dmco));
9565   PetscCall(DMGetFullDM(dmf, &dmfo));
9566   PetscCall(DMSetCoarseDM(dmfo, dmco));
9567   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9568   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9569   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9570   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9571   PetscCall(DMCreateLocalVector(dmc, &clocal));
9572   PetscCall(VecSet(cglobalo, 0.));
9573   PetscCall(VecSet(clocal, 0.));
9574   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9575   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9576   PetscCall(DMCreateLocalVector(dmf, &flocal));
9577   PetscCall(VecSet(fglobal, 0.));
9578   PetscCall(VecSet(fglobalo, 0.));
9579   PetscCall(VecSet(flocal, 0.));
9580   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9581   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9582   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9583   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9584   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9585   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9586   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9587   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9588   *shift = fglobal;
9589   PetscCall(VecDestroy(&flocal));
9590   PetscCall(VecDestroy(&fglobalo));
9591   PetscCall(VecDestroy(&clocal));
9592   PetscCall(VecDestroy(&cglobalo));
9593   PetscCall(VecDestroy(&rscale));
9594   PetscCall(MatDestroy(&interpo));
9595   PetscCall(DMDestroy(&dmfo));
9596   PetscCall(DMDestroy(&dmco));
9597   PetscFunctionReturn(PETSC_SUCCESS);
9598 }
9599 
9600 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9601 {
9602   PetscObject shifto;
9603   Vec         shift;
9604 
9605   PetscFunctionBegin;
9606   if (!interp) {
9607     Vec rscale;
9608 
9609     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9610     PetscCall(VecDestroy(&rscale));
9611   } else {
9612     PetscCall(PetscObjectReference((PetscObject)interp));
9613   }
9614   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9615   if (!shifto) {
9616     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9617     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9618     shifto = (PetscObject)shift;
9619     PetscCall(VecDestroy(&shift));
9620   }
9621   shift = (Vec)shifto;
9622   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9623   PetscCall(VecAXPY(fineSol, 1.0, shift));
9624   PetscCall(MatDestroy(&interp));
9625   PetscFunctionReturn(PETSC_SUCCESS);
9626 }
9627 
9628 /* Pointwise interpolation
9629      Just code FEM for now
9630      u^f = I u^c
9631      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9632      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9633      I_{ij} = psi^f_i phi^c_j
9634 */
9635 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9636 {
9637   PetscSection gsc, gsf;
9638   PetscInt     m, n;
9639   void        *ctx;
9640   DM           cdm;
9641   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9642 
9643   PetscFunctionBegin;
9644   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9645   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9646   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9647   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9648 
9649   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9650   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9651   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9652   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9653   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9654 
9655   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9656   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9657   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9658   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9659   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9660   if (scaling) {
9661     /* Use naive scaling */
9662     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9663   }
9664   PetscFunctionReturn(PETSC_SUCCESS);
9665 }
9666 
9667 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9668 {
9669   VecScatter ctx;
9670 
9671   PetscFunctionBegin;
9672   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9673   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9674   PetscCall(VecScatterDestroy(&ctx));
9675   PetscFunctionReturn(PETSC_SUCCESS);
9676 }
9677 
9678 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[])
9679 {
9680   const PetscInt Nc = uOff[1] - uOff[0];
9681   PetscInt       c;
9682   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9683 }
9684 
9685 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9686 {
9687   DM           dmc;
9688   PetscDS      ds;
9689   Vec          ones, locmass;
9690   IS           cellIS;
9691   PetscFormKey key;
9692   PetscInt     depth;
9693 
9694   PetscFunctionBegin;
9695   PetscCall(DMClone(dm, &dmc));
9696   PetscCall(DMCopyDisc(dm, dmc));
9697   PetscCall(DMGetDS(dmc, &ds));
9698   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9699   PetscCall(DMCreateGlobalVector(dmc, mass));
9700   PetscCall(DMGetLocalVector(dmc, &ones));
9701   PetscCall(DMGetLocalVector(dmc, &locmass));
9702   PetscCall(DMPlexGetDepth(dmc, &depth));
9703   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9704   PetscCall(VecSet(locmass, 0.0));
9705   PetscCall(VecSet(ones, 1.0));
9706   key.label = NULL;
9707   key.value = 0;
9708   key.field = 0;
9709   key.part  = 0;
9710   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9711   PetscCall(ISDestroy(&cellIS));
9712   PetscCall(VecSet(*mass, 0.0));
9713   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9714   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9715   PetscCall(DMRestoreLocalVector(dmc, &ones));
9716   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9717   PetscCall(DMDestroy(&dmc));
9718   PetscFunctionReturn(PETSC_SUCCESS);
9719 }
9720 
9721 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9722 {
9723   PetscSection gsc, gsf;
9724   PetscInt     m, n;
9725   void        *ctx;
9726   DM           cdm;
9727   PetscBool    regular;
9728 
9729   PetscFunctionBegin;
9730   if (dmFine == dmCoarse) {
9731     DM            dmc;
9732     PetscDS       ds;
9733     PetscWeakForm wf;
9734     Vec           u;
9735     IS            cellIS;
9736     PetscFormKey  key;
9737     PetscInt      depth;
9738 
9739     PetscCall(DMClone(dmFine, &dmc));
9740     PetscCall(DMCopyDisc(dmFine, dmc));
9741     PetscCall(DMGetDS(dmc, &ds));
9742     PetscCall(PetscDSGetWeakForm(ds, &wf));
9743     PetscCall(PetscWeakFormClear(wf));
9744     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9745     PetscCall(DMCreateMatrix(dmc, mass));
9746     PetscCall(DMGetLocalVector(dmc, &u));
9747     PetscCall(DMPlexGetDepth(dmc, &depth));
9748     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9749     PetscCall(MatZeroEntries(*mass));
9750     key.label = NULL;
9751     key.value = 0;
9752     key.field = 0;
9753     key.part  = 0;
9754     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9755     PetscCall(ISDestroy(&cellIS));
9756     PetscCall(DMRestoreLocalVector(dmc, &u));
9757     PetscCall(DMDestroy(&dmc));
9758   } else {
9759     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9760     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9761     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9762     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9763 
9764     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9765     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9766     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9767     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9768 
9769     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9770     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9771     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9772     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9773   }
9774   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9775   PetscFunctionReturn(PETSC_SUCCESS);
9776 }
9777 
9778 /*@
9779   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9780 
9781   Input Parameter:
9782 . dm - The `DMPLEX` object
9783 
9784   Output Parameter:
9785 . regular - The flag
9786 
9787   Level: intermediate
9788 
9789 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9790 @*/
9791 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9792 {
9793   PetscFunctionBegin;
9794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9795   PetscValidBoolPointer(regular, 2);
9796   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9797   PetscFunctionReturn(PETSC_SUCCESS);
9798 }
9799 
9800 /*@
9801   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9802 
9803   Input Parameters:
9804 + dm - The `DMPLEX` object
9805 - regular - The flag
9806 
9807   Level: intermediate
9808 
9809 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9810 @*/
9811 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9812 {
9813   PetscFunctionBegin;
9814   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9815   ((DM_Plex *)dm->data)->regularRefinement = regular;
9816   PetscFunctionReturn(PETSC_SUCCESS);
9817 }
9818 
9819 /*@
9820   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9821   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9822 
9823   Not Collective
9824 
9825   Input Parameter:
9826 . dm - The `DMPLEX` object
9827 
9828   Output Parameters:
9829 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9830 - anchorIS - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9831 
9832   Level: intermediate
9833 
9834 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9835 @*/
9836 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9837 {
9838   DM_Plex *plex = (DM_Plex *)dm->data;
9839 
9840   PetscFunctionBegin;
9841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9842   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9843   if (anchorSection) *anchorSection = plex->anchorSection;
9844   if (anchorIS) *anchorIS = plex->anchorIS;
9845   PetscFunctionReturn(PETSC_SUCCESS);
9846 }
9847 
9848 /*@
9849   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9850   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9851   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9852 
9853   Collective
9854 
9855   Input Parameters:
9856 + dm - The `DMPLEX` object
9857 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9858                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9859 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9860 
9861   Level: intermediate
9862 
9863   Notes:
9864   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9865   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9866 
9867   The reference counts of `anchorSection` and `anchorIS` are incremented.
9868 
9869 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9870 @*/
9871 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9872 {
9873   DM_Plex    *plex = (DM_Plex *)dm->data;
9874   PetscMPIInt result;
9875 
9876   PetscFunctionBegin;
9877   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9878   if (anchorSection) {
9879     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9880     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9881     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9882   }
9883   if (anchorIS) {
9884     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9885     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9886     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9887   }
9888 
9889   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9890   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9891   plex->anchorSection = anchorSection;
9892 
9893   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9894   PetscCall(ISDestroy(&plex->anchorIS));
9895   plex->anchorIS = anchorIS;
9896 
9897   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9898     PetscInt        size, a, pStart, pEnd;
9899     const PetscInt *anchors;
9900 
9901     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9902     PetscCall(ISGetLocalSize(anchorIS, &size));
9903     PetscCall(ISGetIndices(anchorIS, &anchors));
9904     for (a = 0; a < size; a++) {
9905       PetscInt p;
9906 
9907       p = anchors[a];
9908       if (p >= pStart && p < pEnd) {
9909         PetscInt dof;
9910 
9911         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9912         if (dof) {
9913           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9914           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9915         }
9916       }
9917     }
9918     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9919   }
9920   /* reset the generic constraints */
9921   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9922   PetscFunctionReturn(PETSC_SUCCESS);
9923 }
9924 
9925 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9926 {
9927   PetscSection anchorSection;
9928   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9929 
9930   PetscFunctionBegin;
9931   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9932   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9933   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9934   PetscCall(PetscSectionGetNumFields(section, &numFields));
9935   if (numFields) {
9936     PetscInt f;
9937     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9938 
9939     for (f = 0; f < numFields; f++) {
9940       PetscInt numComp;
9941 
9942       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9943       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9944     }
9945   }
9946   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9947   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9948   pStart = PetscMax(pStart, sStart);
9949   pEnd   = PetscMin(pEnd, sEnd);
9950   pEnd   = PetscMax(pStart, pEnd);
9951   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9952   for (p = pStart; p < pEnd; p++) {
9953     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9954     if (dof) {
9955       PetscCall(PetscSectionGetDof(section, p, &dof));
9956       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9957       for (f = 0; f < numFields; f++) {
9958         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9959         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9960       }
9961     }
9962   }
9963   PetscCall(PetscSectionSetUp(*cSec));
9964   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9965   PetscFunctionReturn(PETSC_SUCCESS);
9966 }
9967 
9968 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9969 {
9970   PetscSection    aSec;
9971   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9972   const PetscInt *anchors;
9973   PetscInt        numFields, f;
9974   IS              aIS;
9975   MatType         mtype;
9976   PetscBool       iscuda, iskokkos;
9977 
9978   PetscFunctionBegin;
9979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9980   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9981   PetscCall(PetscSectionGetStorageSize(section, &n));
9982   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9983   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9984   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9985   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9986   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9987   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9988   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9989   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9990   else mtype = MATSEQAIJ;
9991   PetscCall(MatSetType(*cMat, mtype));
9992   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9993   PetscCall(ISGetIndices(aIS, &anchors));
9994   /* cSec will be a subset of aSec and section */
9995   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9996   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9997   PetscCall(PetscMalloc1(m + 1, &i));
9998   i[0] = 0;
9999   PetscCall(PetscSectionGetNumFields(section, &numFields));
10000   for (p = pStart; p < pEnd; p++) {
10001     PetscInt rDof, rOff, r;
10002 
10003     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10004     if (!rDof) continue;
10005     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10006     if (numFields) {
10007       for (f = 0; f < numFields; f++) {
10008         annz = 0;
10009         for (r = 0; r < rDof; r++) {
10010           a = anchors[rOff + r];
10011           if (a < sStart || a >= sEnd) continue;
10012           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10013           annz += aDof;
10014         }
10015         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10016         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10017         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10018       }
10019     } else {
10020       annz = 0;
10021       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10022       for (q = 0; q < dof; q++) {
10023         a = anchors[rOff + q];
10024         if (a < sStart || a >= sEnd) continue;
10025         PetscCall(PetscSectionGetDof(section, a, &aDof));
10026         annz += aDof;
10027       }
10028       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10029       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10030       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10031     }
10032   }
10033   nnz = i[m];
10034   PetscCall(PetscMalloc1(nnz, &j));
10035   offset = 0;
10036   for (p = pStart; p < pEnd; p++) {
10037     if (numFields) {
10038       for (f = 0; f < numFields; f++) {
10039         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10040         for (q = 0; q < dof; q++) {
10041           PetscInt rDof, rOff, r;
10042           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10043           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10044           for (r = 0; r < rDof; r++) {
10045             PetscInt s;
10046 
10047             a = anchors[rOff + r];
10048             if (a < sStart || a >= sEnd) continue;
10049             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10050             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10051             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10052           }
10053         }
10054       }
10055     } else {
10056       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10057       for (q = 0; q < dof; q++) {
10058         PetscInt rDof, rOff, r;
10059         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10060         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10061         for (r = 0; r < rDof; r++) {
10062           PetscInt s;
10063 
10064           a = anchors[rOff + r];
10065           if (a < sStart || a >= sEnd) continue;
10066           PetscCall(PetscSectionGetDof(section, a, &aDof));
10067           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10068           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10069         }
10070       }
10071     }
10072   }
10073   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10074   PetscCall(PetscFree(i));
10075   PetscCall(PetscFree(j));
10076   PetscCall(ISRestoreIndices(aIS, &anchors));
10077   PetscFunctionReturn(PETSC_SUCCESS);
10078 }
10079 
10080 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10081 {
10082   DM_Plex     *plex = (DM_Plex *)dm->data;
10083   PetscSection anchorSection, section, cSec;
10084   Mat          cMat;
10085 
10086   PetscFunctionBegin;
10087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10088   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10089   if (anchorSection) {
10090     PetscInt Nf;
10091 
10092     PetscCall(DMGetLocalSection(dm, &section));
10093     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10094     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10095     PetscCall(DMGetNumFields(dm, &Nf));
10096     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10097     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10098     PetscCall(PetscSectionDestroy(&cSec));
10099     PetscCall(MatDestroy(&cMat));
10100   }
10101   PetscFunctionReturn(PETSC_SUCCESS);
10102 }
10103 
10104 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10105 {
10106   IS           subis;
10107   PetscSection section, subsection;
10108 
10109   PetscFunctionBegin;
10110   PetscCall(DMGetLocalSection(dm, &section));
10111   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10112   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10113   /* Create subdomain */
10114   PetscCall(DMPlexFilter(dm, label, value, subdm));
10115   /* Create submodel */
10116   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10117   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10118   PetscCall(DMSetLocalSection(*subdm, subsection));
10119   PetscCall(PetscSectionDestroy(&subsection));
10120   PetscCall(DMCopyDisc(dm, *subdm));
10121   /* Create map from submodel to global model */
10122   if (is) {
10123     PetscSection    sectionGlobal, subsectionGlobal;
10124     IS              spIS;
10125     const PetscInt *spmap;
10126     PetscInt       *subIndices;
10127     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10128     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10129 
10130     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10131     PetscCall(ISGetIndices(spIS, &spmap));
10132     PetscCall(PetscSectionGetNumFields(section, &Nf));
10133     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10134     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10135     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10136     for (p = pStart; p < pEnd; ++p) {
10137       PetscInt gdof, pSubSize = 0;
10138 
10139       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10140       if (gdof > 0) {
10141         for (f = 0; f < Nf; ++f) {
10142           PetscInt fdof, fcdof;
10143 
10144           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10145           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10146           pSubSize += fdof - fcdof;
10147         }
10148         subSize += pSubSize;
10149         if (pSubSize) {
10150           if (bs < 0) {
10151             bs = pSubSize;
10152           } else if (bs != pSubSize) {
10153             /* Layout does not admit a pointwise block size */
10154             bs = 1;
10155           }
10156         }
10157       }
10158     }
10159     /* Must have same blocksize on all procs (some might have no points) */
10160     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10161     bsLocal[1] = bs;
10162     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10163     if (bsMinMax[0] != bsMinMax[1]) {
10164       bs = 1;
10165     } else {
10166       bs = bsMinMax[0];
10167     }
10168     PetscCall(PetscMalloc1(subSize, &subIndices));
10169     for (p = pStart; p < pEnd; ++p) {
10170       PetscInt gdof, goff;
10171 
10172       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10173       if (gdof > 0) {
10174         const PetscInt point = spmap[p];
10175 
10176         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10177         for (f = 0; f < Nf; ++f) {
10178           PetscInt fdof, fcdof, fc, f2, poff = 0;
10179 
10180           /* Can get rid of this loop by storing field information in the global section */
10181           for (f2 = 0; f2 < f; ++f2) {
10182             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10183             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10184             poff += fdof - fcdof;
10185           }
10186           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10187           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10188           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10189         }
10190       }
10191     }
10192     PetscCall(ISRestoreIndices(spIS, &spmap));
10193     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10194     if (bs > 1) {
10195       /* We need to check that the block size does not come from non-contiguous fields */
10196       PetscInt i, j, set = 1;
10197       for (i = 0; i < subSize; i += bs) {
10198         for (j = 0; j < bs; ++j) {
10199           if (subIndices[i + j] != subIndices[i] + j) {
10200             set = 0;
10201             break;
10202           }
10203         }
10204       }
10205       if (set) PetscCall(ISSetBlockSize(*is, bs));
10206     }
10207     /* Attach nullspace */
10208     for (f = 0; f < Nf; ++f) {
10209       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10210       if ((*subdm)->nullspaceConstructors[f]) break;
10211     }
10212     if (f < Nf) {
10213       MatNullSpace nullSpace;
10214       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10215 
10216       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10217       PetscCall(MatNullSpaceDestroy(&nullSpace));
10218     }
10219   }
10220   PetscFunctionReturn(PETSC_SUCCESS);
10221 }
10222 
10223 /*@
10224   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10225 
10226   Input Parameters:
10227 + dm - The `DM`
10228 - dummy - unused argument
10229 
10230   Options Database Key:
10231 . -dm_plex_monitor_throughput - Activate the monitor
10232 
10233   Level: developer
10234 
10235 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10236 @*/
10237 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10238 {
10239 #if defined(PETSC_USE_LOG)
10240   PetscStageLog      stageLog;
10241   PetscLogEvent      event;
10242   PetscLogStage      stage;
10243   PetscEventPerfInfo eventInfo;
10244   PetscReal          cellRate, flopRate;
10245   PetscInt           cStart, cEnd, Nf, N;
10246   const char        *name;
10247 #endif
10248 
10249   PetscFunctionBegin;
10250   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10251 #if defined(PETSC_USE_LOG)
10252   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10253   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10254   PetscCall(DMGetNumFields(dm, &Nf));
10255   PetscCall(PetscLogGetStageLog(&stageLog));
10256   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10257   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10258   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10259   N        = (cEnd - cStart) * Nf * eventInfo.count;
10260   flopRate = eventInfo.flops / eventInfo.time;
10261   cellRate = N / eventInfo.time;
10262   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)));
10263 #else
10264   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10265 #endif
10266   PetscFunctionReturn(PETSC_SUCCESS);
10267 }
10268