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