xref: /petsc/src/dm/impls/plex/plex.c (revision db485b19c7dd0390926fc66ce43f816d51296093)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `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   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(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: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer     - The `PetscViewer` for saving
1954 - sectiondm  - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   Typical calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2026 @*/
2027 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2028 {
2029   PetscBool ishdf5;
2030 
2031   PetscFunctionBegin;
2032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2033   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2034   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2035   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2036   /* Check consistency */
2037   {
2038     PetscSection section;
2039     PetscBool    includesConstraints;
2040     PetscInt     m, m1;
2041 
2042     PetscCall(VecGetLocalSize(vec, &m1));
2043     PetscCall(DMGetGlobalSection(sectiondm, &section));
2044     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2045     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2046     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2047     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2048   }
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2054 #else
2055     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2056 #endif
2057   }
2058   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2059   PetscFunctionReturn(PETSC_SUCCESS);
2060 }
2061 
2062 /*@
2063   DMPlexLocalVectorView - Saves a local vector
2064 
2065   Collective
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2077 
2078   Typical calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2101 @*/
2102 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2110   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2111   /* Check consistency */
2112   {
2113     PetscSection section;
2114     PetscBool    includesConstraints;
2115     PetscInt     m, m1;
2116 
2117     PetscCall(VecGetLocalSize(vec, &m1));
2118     PetscCall(DMGetLocalSection(sectiondm, &section));
2119     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2120     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2121     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2122     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2123   }
2124   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2125   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   if (ishdf5) {
2127 #if defined(PETSC_HAVE_HDF5)
2128     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   if (ishdf5) {
2146 #if defined(PETSC_HAVE_HDF5)
2147     PetscViewerFormat format;
2148     PetscCall(PetscViewerGetFormat(viewer, &format));
2149     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2150       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2151     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2152       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2153     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2154     PetscFunctionReturn(PETSC_SUCCESS);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2159 }
2160 
2161 /*@
2162   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm                - The `DM` into which the topology is loaded
2168 - viewer            - The `PetscViewer` for the saved topology
2169 
2170   Output Parameter:
2171 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2172 
2173   Level: advanced
2174 
2175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective
2207 
2208   Input Parameters:
2209 + dm     - The `DM` into which the coordinates are loaded
2210 . viewer - The `PetscViewer` for the saved coordinates
2211 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2212 
2213   Level: advanced
2214 
2215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2216           `PetscSF`, `PetscViewer`
2217 @*/
2218 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2219 {
2220   PetscBool ishdf5;
2221 
2222   PetscFunctionBegin;
2223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2224   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2225   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscViewerFormat format;
2231     PetscCall(PetscViewerGetFormat(viewer, &format));
2232     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2233       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2234     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2235 #else
2236     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2237 #endif
2238   }
2239   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2240   PetscFunctionReturn(PETSC_SUCCESS);
2241 }
2242 
2243 /*@
2244   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2245 
2246   Collective
2247 
2248   Input Parameters:
2249 + dm     - The `DM` into which the labels are loaded
2250 . viewer - The `PetscViewer` for the saved labels
2251 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2252 
2253   Level: advanced
2254 
2255   Note:
2256   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2259           `PetscSF`, `PetscViewer`
2260 @*/
2261 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscViewerFormat format;
2274 
2275     PetscCall(PetscViewerGetFormat(viewer, &format));
2276     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2277       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2278     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2279 #else
2280     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2281 #endif
2282   }
2283   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   PetscFunctionReturn(PETSC_SUCCESS);
2285 }
2286 
2287 /*@
2288   DMPlexSectionLoad - Loads section into a `DMPLEX`
2289 
2290   Collective
2291 
2292   Input Parameters:
2293 + dm          - The `DM` that represents the topology
2294 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2295 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2296 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2297 
2298   Output Parameters
2299 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2300 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2306 
2307   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2308 
2309   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2310 
2311   Example using 2 processes:
2312 .vb
2313   NX (number of points on dm): 4
2314   sectionA                   : the on-disk section
2315   vecA                       : a vector associated with sectionA
2316   sectionB                   : sectiondm's local section constructed in this function
2317   vecB (local)               : a vector associated with sectiondm's local section
2318   vecB (global)              : a vector associated with sectiondm's global section
2319 
2320                                      rank 0    rank 1
2321   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2322   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2323   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2324   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2325   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2326   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2327   sectionB->atlasDof             :     1 0 1 | 1 3
2328   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2329   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2330   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2331 .ve
2332   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2346   if (localDofSF) PetscValidPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2376 
2377   Typical calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2397           `PetscSF`, `PetscViewer`
2398 @*/
2399 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2407   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetGlobalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective
2439 
2440   Input Parameters:
2441 + dm        - The `DM` that represents the topology
2442 . viewer    - The `PetscViewer` that represents the on-disk vector data
2443 . sectiondm - The `DM` that contains the local section on which vec is defined
2444 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2451 
2452   Typical calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2537   PetscCall(PetscFree(mesh->tetgenOpts));
2538   PetscCall(PetscFree(mesh->triangleOpts));
2539   PetscCall(PetscFree(mesh->transformType));
2540   PetscCall(PetscFree(mesh->distributionName));
2541   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2542   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2543   PetscCall(ISDestroy(&mesh->subpointIS));
2544   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2545   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2546   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2548   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2549   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2550   PetscCall(ISDestroy(&mesh->anchorIS));
2551   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2552   PetscCall(PetscFree(mesh->parents));
2553   PetscCall(PetscFree(mesh->childIDs));
2554   PetscCall(PetscSectionDestroy(&mesh->childSection));
2555   PetscCall(PetscFree(mesh->children));
2556   PetscCall(DMDestroy(&mesh->referenceTree));
2557   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2558   PetscCall(PetscFree(mesh->neighbors));
2559   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2560   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2561   PetscCall(PetscFree(mesh));
2562   PetscFunctionReturn(PETSC_SUCCESS);
2563 }
2564 
2565 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2566 {
2567   PetscSection           sectionGlobal;
2568   PetscInt               bs = -1, mbs;
2569   PetscInt               localSize, localStart = 0;
2570   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2571   MatType                mtype;
2572   ISLocalToGlobalMapping ltog;
2573 
2574   PetscFunctionBegin;
2575   PetscCall(MatInitializePackage());
2576   mtype = dm->mattype;
2577   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2578   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2579   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2580   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2581   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2582   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2583   PetscCall(MatSetType(*J, mtype));
2584   PetscCall(MatSetFromOptions(*J));
2585   PetscCall(MatGetBlockSize(*J, &mbs));
2586   if (mbs > 1) bs = mbs;
2587   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2588   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2589   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2590   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2591   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2593   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2594   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2595   if (!isShell) {
2596     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2597     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2598     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2599 
2600     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2601 
2602     PetscCall(PetscCalloc1(localSize, &pblocks));
2603     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2604     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2605     for (p = pStart; p < pEnd; ++p) {
2606       switch (dm->blocking_type) {
2607       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2608         PetscInt bdof, offset;
2609 
2610         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2611         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2612         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2613         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2614         dof  = dof < 0 ? -(dof + 1) : dof;
2615         bdof = cdof && (dof - cdof) ? 1 : dof;
2616         if (dof) {
2617           if (bs < 0) {
2618             bs = bdof;
2619           } else if (bs != bdof) {
2620             bs = 1;
2621           }
2622         }
2623       } break;
2624       case DM_BLOCKING_FIELD_NODE: {
2625         for (PetscInt field = 0; field < num_fields; field++) {
2626           PetscInt num_comp, bdof, offset;
2627           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2628           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2629           if (dof < 0) continue;
2630           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2631           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2632           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);
2633           PetscInt num_nodes = dof / num_comp;
2634           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2635           // Handle possibly constant block size (unlikely)
2636           bdof = cdof && (dof - cdof) ? 1 : dof;
2637           if (dof) {
2638             if (bs < 0) {
2639               bs = bdof;
2640             } else if (bs != bdof) {
2641               bs = 1;
2642             }
2643           }
2644         }
2645       } break;
2646       }
2647     }
2648     /* Must have same blocksize on all procs (some might have no points) */
2649     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2650     bsLocal[1] = bs;
2651     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2652     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2653     else bs = bsMinMax[0];
2654     bs = PetscMax(1, bs);
2655     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2656     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2657       PetscCall(MatSetBlockSize(*J, bs));
2658       PetscCall(MatSetUp(*J));
2659     } else {
2660       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2661       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2662       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2663     }
2664     { // Consolidate blocks
2665       PetscInt nblocks = 0;
2666       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2667         if (pblocks[i] == 0) continue;
2668         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2669         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]);
2670       }
2671       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2672     }
2673     PetscCall(PetscFree(pblocks));
2674   }
2675   PetscCall(MatSetDM(*J, dm));
2676   PetscFunctionReturn(PETSC_SUCCESS);
2677 }
2678 
2679 /*@
2680   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2681 
2682   Not Collective
2683 
2684   Input Parameter:
2685 . mesh - The `DMPLEX`
2686 
2687   Output Parameter:
2688 . subsection - The subdomain section
2689 
2690   Level: developer
2691 
2692 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2693 @*/
2694 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2695 {
2696   DM_Plex *mesh = (DM_Plex *)dm->data;
2697 
2698   PetscFunctionBegin;
2699   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2700   if (!mesh->subdomainSection) {
2701     PetscSection section;
2702     PetscSF      sf;
2703 
2704     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2705     PetscCall(DMGetLocalSection(dm, &section));
2706     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2707     PetscCall(PetscSFDestroy(&sf));
2708   }
2709   *subsection = mesh->subdomainSection;
2710   PetscFunctionReturn(PETSC_SUCCESS);
2711 }
2712 
2713 /*@
2714   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2715 
2716   Not Collective
2717 
2718   Input Parameter:
2719 . mesh - The `DMPLEX`
2720 
2721   Output Parameters:
2722 + pStart - The first mesh point
2723 - pEnd   - The upper bound for mesh points
2724 
2725   Level: beginner
2726 
2727 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2728 @*/
2729 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2730 {
2731   DM_Plex *mesh = (DM_Plex *)dm->data;
2732 
2733   PetscFunctionBegin;
2734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2735   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2736   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2737   PetscFunctionReturn(PETSC_SUCCESS);
2738 }
2739 
2740 /*@
2741   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2742 
2743   Not Collective
2744 
2745   Input Parameters:
2746 + mesh - The `DMPLEX`
2747 . pStart - The first mesh point
2748 - pEnd   - The upper bound for mesh points
2749 
2750   Level: beginner
2751 
2752 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2753 @*/
2754 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2755 {
2756   DM_Plex *mesh = (DM_Plex *)dm->data;
2757 
2758   PetscFunctionBegin;
2759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2760   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2761   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2762   PetscFunctionReturn(PETSC_SUCCESS);
2763 }
2764 
2765 /*@
2766   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2767 
2768   Not Collective
2769 
2770   Input Parameters:
2771 + mesh - The `DMPLEX`
2772 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2773 
2774   Output Parameter:
2775 . size - The cone size for point `p`
2776 
2777   Level: beginner
2778 
2779 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2780 @*/
2781 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2782 {
2783   DM_Plex *mesh = (DM_Plex *)dm->data;
2784 
2785   PetscFunctionBegin;
2786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2787   PetscValidIntPointer(size, 3);
2788   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2789   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2790   PetscFunctionReturn(PETSC_SUCCESS);
2791 }
2792 
2793 /*@
2794   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2795 
2796   Not Collective
2797 
2798   Input Parameters:
2799 + mesh - The `DMPLEX`
2800 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2801 - size - The cone size for point `p`
2802 
2803   Level: beginner
2804 
2805   Note:
2806   This should be called after `DMPlexSetChart()`.
2807 
2808 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2809 @*/
2810 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2811 {
2812   DM_Plex *mesh = (DM_Plex *)dm->data;
2813 
2814   PetscFunctionBegin;
2815   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2816   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2817   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2818   PetscFunctionReturn(PETSC_SUCCESS);
2819 }
2820 
2821 /*@C
2822   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2823 
2824   Not Collective
2825 
2826   Input Parameters:
2827 + dm - The `DMPLEX`
2828 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2829 
2830   Output Parameter:
2831 . cone - An array of points which are on the in-edges for point `p`
2832 
2833   Level: beginner
2834 
2835   Fortran Note:
2836   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2837   `DMPlexRestoreCone()` is not needed/available in C.
2838 
2839 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2840 @*/
2841 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2842 {
2843   DM_Plex *mesh = (DM_Plex *)dm->data;
2844   PetscInt off;
2845 
2846   PetscFunctionBegin;
2847   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2848   PetscValidPointer(cone, 3);
2849   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2850   *cone = &mesh->cones[off];
2851   PetscFunctionReturn(PETSC_SUCCESS);
2852 }
2853 
2854 /*@C
2855   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2856 
2857   Not Collective
2858 
2859   Input Parameters:
2860 + dm - The `DMPLEX`
2861 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2862 
2863   Output Parameters:
2864 + pConesSection - `PetscSection` describing the layout of `pCones`
2865 - pCones - An array of points which are on the in-edges for the point set `p`
2866 
2867   Level: intermediate
2868 
2869 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2870 @*/
2871 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2872 {
2873   PetscSection cs, newcs;
2874   PetscInt    *cones;
2875   PetscInt    *newarr = NULL;
2876   PetscInt     n;
2877 
2878   PetscFunctionBegin;
2879   PetscCall(DMPlexGetCones(dm, &cones));
2880   PetscCall(DMPlexGetConeSection(dm, &cs));
2881   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2882   if (pConesSection) *pConesSection = newcs;
2883   if (pCones) {
2884     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2885     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2886   }
2887   PetscFunctionReturn(PETSC_SUCCESS);
2888 }
2889 
2890 /*@
2891   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2892 
2893   Not Collective
2894 
2895   Input Parameters:
2896 + dm - The `DMPLEX`
2897 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2898 
2899   Output Parameter:
2900 . expandedPoints - An array of vertices recursively expanded from input points
2901 
2902   Level: advanced
2903 
2904   Notes:
2905   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2906 
2907   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2908 
2909 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2910           `DMPlexGetDepth()`, `IS`
2911 @*/
2912 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2913 {
2914   IS      *expandedPointsAll;
2915   PetscInt depth;
2916 
2917   PetscFunctionBegin;
2918   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2919   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2920   PetscValidPointer(expandedPoints, 3);
2921   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2922   *expandedPoints = expandedPointsAll[0];
2923   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2924   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2925   PetscFunctionReturn(PETSC_SUCCESS);
2926 }
2927 
2928 /*@
2929   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).
2930 
2931   Not Collective
2932 
2933   Input Parameters:
2934 + dm - The `DMPLEX`
2935 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2936 
2937   Output Parameters:
2938 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2939 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2940 - sections - (optional) An array of sections which describe mappings from points to their cone points
2941 
2942   Level: advanced
2943 
2944   Notes:
2945   Like `DMPlexGetConeTuple()` but recursive.
2946 
2947   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.
2948   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2949 
2950   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:
2951   (1) DAG points in expandedPoints[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2952   (2) DAG points in expandedPoints[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2953 
2954 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2955           `DMPlexGetDepth()`, `PetscSection`, `IS`
2956 @*/
2957 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2958 {
2959   const PetscInt *arr0 = NULL, *cone = NULL;
2960   PetscInt       *arr = NULL, *newarr = NULL;
2961   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2962   IS             *expandedPoints_;
2963   PetscSection   *sections_;
2964 
2965   PetscFunctionBegin;
2966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2967   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2968   if (depth) PetscValidIntPointer(depth, 3);
2969   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2970   if (sections) PetscValidPointer(sections, 5);
2971   PetscCall(ISGetLocalSize(points, &n));
2972   PetscCall(ISGetIndices(points, &arr0));
2973   PetscCall(DMPlexGetDepth(dm, &depth_));
2974   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2975   PetscCall(PetscCalloc1(depth_, &sections_));
2976   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2977   for (d = depth_ - 1; d >= 0; d--) {
2978     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2979     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2980     for (i = 0; i < n; i++) {
2981       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2982       if (arr[i] >= start && arr[i] < end) {
2983         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2984         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2985       } else {
2986         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2987       }
2988     }
2989     PetscCall(PetscSectionSetUp(sections_[d]));
2990     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2991     PetscCall(PetscMalloc1(newn, &newarr));
2992     for (i = 0; i < n; i++) {
2993       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2994       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2995       if (cn > 1) {
2996         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2997         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2998       } else {
2999         newarr[co] = arr[i];
3000       }
3001     }
3002     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3003     arr = newarr;
3004     n   = newn;
3005   }
3006   PetscCall(ISRestoreIndices(points, &arr0));
3007   *depth = depth_;
3008   if (expandedPoints) *expandedPoints = expandedPoints_;
3009   else {
3010     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3011     PetscCall(PetscFree(expandedPoints_));
3012   }
3013   if (sections) *sections = sections_;
3014   else {
3015     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3016     PetscCall(PetscFree(sections_));
3017   }
3018   PetscFunctionReturn(PETSC_SUCCESS);
3019 }
3020 
3021 /*@
3022   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3023 
3024   Not Collective
3025 
3026   Input Parameters:
3027 + dm - The `DMPLEX`
3028 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3029 
3030   Output Parameters:
3031 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3032 . expandedPoints - (optional) An array of recursively expanded cones
3033 - sections - (optional) An array of sections which describe mappings from points to their cone points
3034 
3035   Level: advanced
3036 
3037   Note:
3038   See `DMPlexGetConeRecursive()`
3039 
3040 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3041           `DMPlexGetDepth()`, `IS`, `PetscSection`
3042 @*/
3043 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3044 {
3045   PetscInt d, depth_;
3046 
3047   PetscFunctionBegin;
3048   PetscCall(DMPlexGetDepth(dm, &depth_));
3049   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3050   if (depth) *depth = 0;
3051   if (expandedPoints) {
3052     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3053     PetscCall(PetscFree(*expandedPoints));
3054   }
3055   if (sections) {
3056     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3057     PetscCall(PetscFree(*sections));
3058   }
3059   PetscFunctionReturn(PETSC_SUCCESS);
3060 }
3061 
3062 /*@
3063   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
3064 
3065   Not Collective
3066 
3067   Input Parameters:
3068 + mesh - The `DMPLEX`
3069 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3070 - cone - An array of points which are on the in-edges for point `p`
3071 
3072   Level: beginner
3073 
3074   Note:
3075   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3076 
3077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3078 @*/
3079 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3080 {
3081   DM_Plex *mesh = (DM_Plex *)dm->data;
3082   PetscInt dof, off, c;
3083 
3084   PetscFunctionBegin;
3085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3086   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3087   if (dof) PetscValidIntPointer(cone, 3);
3088   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3089   if (PetscDefined(USE_DEBUG)) {
3090     PetscInt pStart, pEnd;
3091     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
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   } else {
3098     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3099   }
3100   PetscFunctionReturn(PETSC_SUCCESS);
3101 }
3102 
3103 /*@C
3104   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3105 
3106   Not Collective
3107 
3108   Input Parameters:
3109 + mesh - The `DMPLEX`
3110 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3111 
3112   Output Parameter:
3113 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3114                     integer giving the prescription for cone traversal.
3115 
3116   Level: beginner
3117 
3118   Note:
3119   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3120   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3121   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3122   with the identity.
3123 
3124   Fortran Note:
3125   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3126   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3127 
3128 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3129 @*/
3130 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3131 {
3132   DM_Plex *mesh = (DM_Plex *)dm->data;
3133   PetscInt off;
3134 
3135   PetscFunctionBegin;
3136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3137   if (PetscDefined(USE_DEBUG)) {
3138     PetscInt dof;
3139     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3140     if (dof) PetscValidPointer(coneOrientation, 3);
3141   }
3142   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3143 
3144   *coneOrientation = &mesh->coneOrientations[off];
3145   PetscFunctionReturn(PETSC_SUCCESS);
3146 }
3147 
3148 /*@
3149   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3150 
3151   Not Collective
3152 
3153   Input Parameters:
3154 + mesh - The `DMPLEX`
3155 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3156 - coneOrientation - An array of orientations
3157 
3158   Level: beginner
3159 
3160   Notes:
3161   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3162 
3163   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3164 
3165 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3166 @*/
3167 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3168 {
3169   DM_Plex *mesh = (DM_Plex *)dm->data;
3170   PetscInt pStart, pEnd;
3171   PetscInt dof, off, c;
3172 
3173   PetscFunctionBegin;
3174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3175   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3176   if (dof) PetscValidIntPointer(coneOrientation, 3);
3177   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3178   if (PetscDefined(USE_DEBUG)) {
3179     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3180     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);
3181     for (c = 0; c < dof; ++c) {
3182       PetscInt cdof, o = coneOrientation[c];
3183 
3184       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3185       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);
3186       mesh->coneOrientations[off + c] = o;
3187     }
3188   } else {
3189     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3190   }
3191   PetscFunctionReturn(PETSC_SUCCESS);
3192 }
3193 
3194 /*@
3195   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3196 
3197   Not Collective
3198 
3199   Input Parameters:
3200 + mesh - The `DMPLEX`
3201 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3202 . conePos - The local index in the cone where the point should be put
3203 - conePoint - The mesh point to insert
3204 
3205   Level: beginner
3206 
3207 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3208 @*/
3209 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3210 {
3211   DM_Plex *mesh = (DM_Plex *)dm->data;
3212   PetscInt pStart, pEnd;
3213   PetscInt dof, off;
3214 
3215   PetscFunctionBegin;
3216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3217   if (PetscDefined(USE_DEBUG)) {
3218     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3219     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);
3220     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);
3221     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3222     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);
3223   }
3224   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3225   mesh->cones[off + conePos] = conePoint;
3226   PetscFunctionReturn(PETSC_SUCCESS);
3227 }
3228 
3229 /*@
3230   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3231 
3232   Not Collective
3233 
3234   Input Parameters:
3235 + mesh - The `DMPLEX`
3236 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3237 . conePos - The local index in the cone where the point should be put
3238 - coneOrientation - The point orientation to insert
3239 
3240   Level: beginner
3241 
3242   Note:
3243   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3244 
3245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3246 @*/
3247 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3248 {
3249   DM_Plex *mesh = (DM_Plex *)dm->data;
3250   PetscInt pStart, pEnd;
3251   PetscInt dof, off;
3252 
3253   PetscFunctionBegin;
3254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3255   if (PetscDefined(USE_DEBUG)) {
3256     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3257     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);
3258     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3259     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);
3260   }
3261   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3262   mesh->coneOrientations[off + conePos] = coneOrientation;
3263   PetscFunctionReturn(PETSC_SUCCESS);
3264 }
3265 
3266 /*@C
3267   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3268 
3269   Not collective
3270 
3271   Input Parameters:
3272 + dm - The DMPlex
3273 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3274 
3275   Output Parameters:
3276 + cone - An array of points which are on the in-edges for point `p`
3277 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3278         integer giving the prescription for cone traversal.
3279 
3280   Level: beginner
3281 
3282   Notes:
3283   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3284   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3285   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3286   with the identity.
3287 
3288   Fortran Notes:
3289   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3290   `DMPlexRestoreCone()` is not needed/available in C.
3291 
3292 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3293 @*/
3294 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3295 {
3296   DM_Plex *mesh = (DM_Plex *)dm->data;
3297 
3298   PetscFunctionBegin;
3299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3300   if (mesh->tr) {
3301     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3302   } else {
3303     PetscInt off;
3304     if (PetscDefined(USE_DEBUG)) {
3305       PetscInt dof;
3306       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3307       if (dof) {
3308         if (cone) PetscValidPointer(cone, 3);
3309         if (ornt) PetscValidPointer(ornt, 4);
3310       }
3311     }
3312     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3313     if (cone) *cone = &mesh->cones[off];
3314     if (ornt) *ornt = &mesh->coneOrientations[off];
3315   }
3316   PetscFunctionReturn(PETSC_SUCCESS);
3317 }
3318 
3319 /*@C
3320   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3321 
3322   Not Collective
3323 
3324   Input Parameters:
3325 + dm - The DMPlex
3326 . p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3327 . cone - An array of points which are on the in-edges for point p
3328 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3329         integer giving the prescription for cone traversal.
3330 
3331   Level: beginner
3332 
3333   Notes:
3334   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3335   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3336   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3337   with the identity.
3338 
3339   Fortran Note:
3340   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3341   `DMPlexRestoreCone()` is not needed/available in C.
3342 
3343 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3344 @*/
3345 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3346 {
3347   DM_Plex *mesh = (DM_Plex *)dm->data;
3348 
3349   PetscFunctionBegin;
3350   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3351   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3352   PetscFunctionReturn(PETSC_SUCCESS);
3353 }
3354 
3355 /*@
3356   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3357 
3358   Not Collective
3359 
3360   Input Parameters:
3361 + mesh - The `DMPLEX`
3362 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3363 
3364   Output Parameter:
3365 . size - The support size for point `p`
3366 
3367   Level: beginner
3368 
3369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3370 @*/
3371 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3372 {
3373   DM_Plex *mesh = (DM_Plex *)dm->data;
3374 
3375   PetscFunctionBegin;
3376   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3377   PetscValidIntPointer(size, 3);
3378   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3379   PetscFunctionReturn(PETSC_SUCCESS);
3380 }
3381 
3382 /*@
3383   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3384 
3385   Not Collective
3386 
3387   Input Parameters:
3388 + mesh - The `DMPLEX`
3389 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3390 - size - The support size for point `p`
3391 
3392   Level: beginner
3393 
3394   Note:
3395   This should be called after `DMPlexSetChart()`.
3396 
3397 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3398 @*/
3399 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3400 {
3401   DM_Plex *mesh = (DM_Plex *)dm->data;
3402 
3403   PetscFunctionBegin;
3404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3405   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3406   PetscFunctionReturn(PETSC_SUCCESS);
3407 }
3408 
3409 /*@C
3410   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3411 
3412   Not Collective
3413 
3414   Input Parameters:
3415 + mesh - The `DMPLEX`
3416 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3417 
3418   Output Parameter:
3419 . support - An array of points which are on the out-edges for point `p`
3420 
3421   Level: beginner
3422 
3423   Fortran Note:
3424   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3425   `DMPlexRestoreSupport()` is not needed/available in C.
3426 
3427 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3428 @*/
3429 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3430 {
3431   DM_Plex *mesh = (DM_Plex *)dm->data;
3432   PetscInt off;
3433 
3434   PetscFunctionBegin;
3435   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3436   PetscValidPointer(support, 3);
3437   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3438   *support = &mesh->supports[off];
3439   PetscFunctionReturn(PETSC_SUCCESS);
3440 }
3441 
3442 /*@
3443   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3444 
3445   Not Collective
3446 
3447   Input Parameters:
3448 + mesh - The `DMPLEX`
3449 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3450 - support - An array of points which are on the out-edges for point `p`
3451 
3452   Level: beginner
3453 
3454   Note:
3455   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3456 
3457 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3458 @*/
3459 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3460 {
3461   DM_Plex *mesh = (DM_Plex *)dm->data;
3462   PetscInt pStart, pEnd;
3463   PetscInt dof, off, c;
3464 
3465   PetscFunctionBegin;
3466   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3467   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3468   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3469   if (dof) PetscValidIntPointer(support, 3);
3470   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3471   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);
3472   for (c = 0; c < dof; ++c) {
3473     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);
3474     mesh->supports[off + c] = support[c];
3475   }
3476   PetscFunctionReturn(PETSC_SUCCESS);
3477 }
3478 
3479 /*@
3480   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3481 
3482   Not Collective
3483 
3484   Input Parameters:
3485 + mesh - The `DMPLEX`
3486 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3487 . supportPos - The local index in the cone where the point should be put
3488 - supportPoint - The mesh point to insert
3489 
3490   Level: beginner
3491 
3492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3493 @*/
3494 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3495 {
3496   DM_Plex *mesh = (DM_Plex *)dm->data;
3497   PetscInt pStart, pEnd;
3498   PetscInt dof, off;
3499 
3500   PetscFunctionBegin;
3501   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3502   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3503   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3504   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3505   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);
3506   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);
3507   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);
3508   mesh->supports[off + supportPos] = supportPoint;
3509   PetscFunctionReturn(PETSC_SUCCESS);
3510 }
3511 
3512 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3513 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3514 {
3515   switch (ct) {
3516   case DM_POLYTOPE_SEGMENT:
3517     if (o == -1) return -2;
3518     break;
3519   case DM_POLYTOPE_TRIANGLE:
3520     if (o == -3) return -1;
3521     if (o == -2) return -3;
3522     if (o == -1) return -2;
3523     break;
3524   case DM_POLYTOPE_QUADRILATERAL:
3525     if (o == -4) return -2;
3526     if (o == -3) return -1;
3527     if (o == -2) return -4;
3528     if (o == -1) return -3;
3529     break;
3530   default:
3531     return o;
3532   }
3533   return o;
3534 }
3535 
3536 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3537 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3538 {
3539   switch (ct) {
3540   case DM_POLYTOPE_SEGMENT:
3541     if ((o == -2) || (o == 1)) return -1;
3542     if (o == -1) return 0;
3543     break;
3544   case DM_POLYTOPE_TRIANGLE:
3545     if (o == -3) return -2;
3546     if (o == -2) return -1;
3547     if (o == -1) return -3;
3548     break;
3549   case DM_POLYTOPE_QUADRILATERAL:
3550     if (o == -4) return -2;
3551     if (o == -3) return -1;
3552     if (o == -2) return -4;
3553     if (o == -1) return -3;
3554     break;
3555   default:
3556     return o;
3557   }
3558   return o;
3559 }
3560 
3561 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3562 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3563 {
3564   PetscInt pStart, pEnd, p;
3565 
3566   PetscFunctionBegin;
3567   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3568   for (p = pStart; p < pEnd; ++p) {
3569     const PetscInt *cone, *ornt;
3570     PetscInt        coneSize, c;
3571 
3572     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3573     PetscCall(DMPlexGetCone(dm, p, &cone));
3574     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3575     for (c = 0; c < coneSize; ++c) {
3576       DMPolytopeType ct;
3577       const PetscInt o = ornt[c];
3578 
3579       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3580       switch (ct) {
3581       case DM_POLYTOPE_SEGMENT:
3582         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3583         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3584         break;
3585       case DM_POLYTOPE_TRIANGLE:
3586         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3587         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3588         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3589         break;
3590       case DM_POLYTOPE_QUADRILATERAL:
3591         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3592         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3593         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3594         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3595         break;
3596       default:
3597         break;
3598       }
3599     }
3600   }
3601   PetscFunctionReturn(PETSC_SUCCESS);
3602 }
3603 
3604 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3605 {
3606   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3607   PetscInt       *closure;
3608   const PetscInt *tmp = NULL, *tmpO = NULL;
3609   PetscInt        off = 0, tmpSize, t;
3610 
3611   PetscFunctionBeginHot;
3612   if (ornt) {
3613     PetscCall(DMPlexGetCellType(dm, p, &ct));
3614     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3615   }
3616   if (*points) {
3617     closure = *points;
3618   } else {
3619     PetscInt maxConeSize, maxSupportSize;
3620     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3621     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3622   }
3623   if (useCone) {
3624     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3625     PetscCall(DMPlexGetCone(dm, p, &tmp));
3626     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3627   } else {
3628     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3629     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3630   }
3631   if (ct == DM_POLYTOPE_UNKNOWN) {
3632     closure[off++] = p;
3633     closure[off++] = 0;
3634     for (t = 0; t < tmpSize; ++t) {
3635       closure[off++] = tmp[t];
3636       closure[off++] = tmpO ? tmpO[t] : 0;
3637     }
3638   } else {
3639     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3640 
3641     /* We assume that cells with a valid type have faces with a valid type */
3642     closure[off++] = p;
3643     closure[off++] = ornt;
3644     for (t = 0; t < tmpSize; ++t) {
3645       DMPolytopeType ft;
3646 
3647       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3648       closure[off++] = tmp[arr[t]];
3649       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3650     }
3651   }
3652   if (numPoints) *numPoints = tmpSize + 1;
3653   if (points) *points = closure;
3654   PetscFunctionReturn(PETSC_SUCCESS);
3655 }
3656 
3657 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3658 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3659 {
3660   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3661   const PetscInt *cone, *ornt;
3662   PetscInt       *pts, *closure = NULL;
3663   DMPolytopeType  ft;
3664   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3665   PetscInt        dim, coneSize, c, d, clSize, cl;
3666 
3667   PetscFunctionBeginHot;
3668   PetscCall(DMGetDimension(dm, &dim));
3669   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3670   PetscCall(DMPlexGetCone(dm, point, &cone));
3671   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3672   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3673   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3674   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3675   maxSize       = PetscMax(coneSeries, supportSeries);
3676   if (*points) {
3677     pts = *points;
3678   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3679   c        = 0;
3680   pts[c++] = point;
3681   pts[c++] = o;
3682   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3683   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3684   for (cl = 0; cl < clSize * 2; cl += 2) {
3685     pts[c++] = closure[cl];
3686     pts[c++] = closure[cl + 1];
3687   }
3688   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3689   for (cl = 0; cl < clSize * 2; cl += 2) {
3690     pts[c++] = closure[cl];
3691     pts[c++] = closure[cl + 1];
3692   }
3693   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3694   for (d = 2; d < coneSize; ++d) {
3695     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3696     pts[c++] = cone[arr[d * 2 + 0]];
3697     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3698   }
3699   if (dim >= 3) {
3700     for (d = 2; d < coneSize; ++d) {
3701       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3702       const PetscInt *fcone, *fornt;
3703       PetscInt        fconeSize, fc, i;
3704 
3705       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3706       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3707       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3708       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3709       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3710       for (fc = 0; fc < fconeSize; ++fc) {
3711         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3712         const PetscInt co = farr[fc * 2 + 1];
3713 
3714         for (i = 0; i < c; i += 2)
3715           if (pts[i] == cp) break;
3716         if (i == c) {
3717           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3718           pts[c++] = cp;
3719           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3720         }
3721       }
3722     }
3723   }
3724   *numPoints = c / 2;
3725   *points    = pts;
3726   PetscFunctionReturn(PETSC_SUCCESS);
3727 }
3728 
3729 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3730 {
3731   DMPolytopeType ct;
3732   PetscInt      *closure, *fifo;
3733   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3734   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3735   PetscInt       depth, maxSize;
3736 
3737   PetscFunctionBeginHot;
3738   PetscCall(DMPlexGetDepth(dm, &depth));
3739   if (depth == 1) {
3740     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3741     PetscFunctionReturn(PETSC_SUCCESS);
3742   }
3743   PetscCall(DMPlexGetCellType(dm, p, &ct));
3744   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3745   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3746     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3747     PetscFunctionReturn(PETSC_SUCCESS);
3748   }
3749   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3750   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3751   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3752   maxSize       = PetscMax(coneSeries, supportSeries);
3753   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3754   if (*points) {
3755     closure = *points;
3756   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3757   closure[closureSize++] = p;
3758   closure[closureSize++] = ornt;
3759   fifo[fifoSize++]       = p;
3760   fifo[fifoSize++]       = ornt;
3761   fifo[fifoSize++]       = ct;
3762   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3763   while (fifoSize - fifoStart) {
3764     const PetscInt       q    = fifo[fifoStart++];
3765     const PetscInt       o    = fifo[fifoStart++];
3766     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3767     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3768     const PetscInt      *tmp, *tmpO;
3769     PetscInt             tmpSize, t;
3770 
3771     if (PetscDefined(USE_DEBUG)) {
3772       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3773       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);
3774     }
3775     if (useCone) {
3776       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3777       PetscCall(DMPlexGetCone(dm, q, &tmp));
3778       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3779     } else {
3780       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3781       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3782       tmpO = NULL;
3783     }
3784     for (t = 0; t < tmpSize; ++t) {
3785       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3786       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3787       const PetscInt cp = tmp[ip];
3788       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3789       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3790       PetscInt       c;
3791 
3792       /* Check for duplicate */
3793       for (c = 0; c < closureSize; c += 2) {
3794         if (closure[c] == cp) break;
3795       }
3796       if (c == closureSize) {
3797         closure[closureSize++] = cp;
3798         closure[closureSize++] = co;
3799         fifo[fifoSize++]       = cp;
3800         fifo[fifoSize++]       = co;
3801         fifo[fifoSize++]       = ct;
3802       }
3803     }
3804   }
3805   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3806   if (numPoints) *numPoints = closureSize / 2;
3807   if (points) *points = closure;
3808   PetscFunctionReturn(PETSC_SUCCESS);
3809 }
3810 
3811 /*@C
3812   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3813 
3814   Not Collective
3815 
3816   Input Parameters:
3817 + dm      - The `DMPLEX`
3818 . p       - The mesh point
3819 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3820 
3821   Input/Output Parameter:
3822 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3823            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3824 
3825   Output Parameter:
3826 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3827 
3828   Level: beginner
3829 
3830   Note:
3831   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3832 
3833   Fortran Note:
3834   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3835 
3836 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3837 @*/
3838 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3839 {
3840   PetscFunctionBeginHot;
3841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3842   if (numPoints) PetscValidIntPointer(numPoints, 4);
3843   if (points) PetscValidPointer(points, 5);
3844   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3845   PetscFunctionReturn(PETSC_SUCCESS);
3846 }
3847 
3848 /*@C
3849   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3850 
3851   Not Collective
3852 
3853   Input Parameters:
3854 + dm        - The `DMPLEX`
3855 . p         - The mesh point
3856 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3857 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3858 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3859 
3860   Level: beginner
3861 
3862   Note:
3863   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3864 
3865 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3866 @*/
3867 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3868 {
3869   PetscFunctionBeginHot;
3870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3871   if (numPoints) *numPoints = 0;
3872   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3873   PetscFunctionReturn(PETSC_SUCCESS);
3874 }
3875 
3876 /*@
3877   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3878 
3879   Not Collective
3880 
3881   Input Parameter:
3882 . mesh - The `DMPLEX`
3883 
3884   Output Parameters:
3885 + maxConeSize - The maximum number of in-edges
3886 - maxSupportSize - The maximum number of out-edges
3887 
3888   Level: beginner
3889 
3890 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3891 @*/
3892 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3893 {
3894   DM_Plex *mesh = (DM_Plex *)dm->data;
3895 
3896   PetscFunctionBegin;
3897   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3898   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3899   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3900   PetscFunctionReturn(PETSC_SUCCESS);
3901 }
3902 
3903 PetscErrorCode DMSetUp_Plex(DM dm)
3904 {
3905   DM_Plex *mesh = (DM_Plex *)dm->data;
3906   PetscInt size, maxSupportSize;
3907 
3908   PetscFunctionBegin;
3909   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3910   PetscCall(PetscSectionSetUp(mesh->coneSection));
3911   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3912   PetscCall(PetscMalloc1(size, &mesh->cones));
3913   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3914   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3915   if (maxSupportSize) {
3916     PetscCall(PetscSectionSetUp(mesh->supportSection));
3917     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3918     PetscCall(PetscMalloc1(size, &mesh->supports));
3919   }
3920   PetscFunctionReturn(PETSC_SUCCESS);
3921 }
3922 
3923 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3924 {
3925   PetscFunctionBegin;
3926   if (subdm) PetscCall(DMClone(dm, subdm));
3927   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3928   if (subdm) (*subdm)->useNatural = dm->useNatural;
3929   if (dm->useNatural && dm->sfMigration) {
3930     PetscSF sfNatural;
3931 
3932     (*subdm)->sfMigration = dm->sfMigration;
3933     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3934     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3935     (*subdm)->sfNatural = sfNatural;
3936   }
3937   PetscFunctionReturn(PETSC_SUCCESS);
3938 }
3939 
3940 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3941 {
3942   PetscInt i = 0;
3943 
3944   PetscFunctionBegin;
3945   PetscCall(DMClone(dms[0], superdm));
3946   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3947   (*superdm)->useNatural = PETSC_FALSE;
3948   for (i = 0; i < len; i++) {
3949     if (dms[i]->useNatural && dms[i]->sfMigration) {
3950       PetscSF sfNatural;
3951 
3952       (*superdm)->sfMigration = dms[i]->sfMigration;
3953       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3954       (*superdm)->useNatural = PETSC_TRUE;
3955       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3956       (*superdm)->sfNatural = sfNatural;
3957       break;
3958     }
3959   }
3960   PetscFunctionReturn(PETSC_SUCCESS);
3961 }
3962 
3963 /*@
3964   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3965 
3966   Not Collective
3967 
3968   Input Parameter:
3969 . mesh - The `DMPLEX`
3970 
3971   Level: beginner
3972 
3973   Note:
3974   This should be called after all calls to `DMPlexSetCone()`
3975 
3976 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3977 @*/
3978 PetscErrorCode DMPlexSymmetrize(DM dm)
3979 {
3980   DM_Plex  *mesh = (DM_Plex *)dm->data;
3981   PetscInt *offsets;
3982   PetscInt  supportSize;
3983   PetscInt  pStart, pEnd, p;
3984 
3985   PetscFunctionBegin;
3986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3987   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3988   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3989   /* Calculate support sizes */
3990   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3991   for (p = pStart; p < pEnd; ++p) {
3992     PetscInt dof, off, c;
3993 
3994     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3995     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3996     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3997   }
3998   PetscCall(PetscSectionSetUp(mesh->supportSection));
3999   /* Calculate supports */
4000   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4001   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4002   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4003   for (p = pStart; p < pEnd; ++p) {
4004     PetscInt dof, off, c;
4005 
4006     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4007     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4008     for (c = off; c < off + dof; ++c) {
4009       const PetscInt q = mesh->cones[c];
4010       PetscInt       offS;
4011 
4012       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4013 
4014       mesh->supports[offS + offsets[q]] = p;
4015       ++offsets[q];
4016     }
4017   }
4018   PetscCall(PetscFree(offsets));
4019   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4020   PetscFunctionReturn(PETSC_SUCCESS);
4021 }
4022 
4023 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4024 {
4025   IS stratumIS;
4026 
4027   PetscFunctionBegin;
4028   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4029   if (PetscDefined(USE_DEBUG)) {
4030     PetscInt  qStart, qEnd, numLevels, level;
4031     PetscBool overlap = PETSC_FALSE;
4032     PetscCall(DMLabelGetNumValues(label, &numLevels));
4033     for (level = 0; level < numLevels; level++) {
4034       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4035       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4036         overlap = PETSC_TRUE;
4037         break;
4038       }
4039     }
4040     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);
4041   }
4042   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4043   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4044   PetscCall(ISDestroy(&stratumIS));
4045   PetscFunctionReturn(PETSC_SUCCESS);
4046 }
4047 
4048 /*@
4049   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4050   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4051   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4052   the DAG.
4053 
4054   Collective
4055 
4056   Input Parameter:
4057 . mesh - The `DMPLEX`
4058 
4059   Level: beginner
4060 
4061   Notes:
4062   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4063   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4064   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4065   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4066   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4067 
4068   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4069   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4070   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
4071   to interpolate only that one (e0), so that
4072 .vb
4073   cone(c0) = {e0, v2}
4074   cone(e0) = {v0, v1}
4075 .ve
4076   If `DMPlexStratify()` is run on this mesh, it will give depths
4077 .vb
4078    depth 0 = {v0, v1, v2}
4079    depth 1 = {e0, c0}
4080 .ve
4081   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4082 
4083   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4084 
4085 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4086 @*/
4087 PetscErrorCode DMPlexStratify(DM dm)
4088 {
4089   DM_Plex *mesh = (DM_Plex *)dm->data;
4090   DMLabel  label;
4091   PetscInt pStart, pEnd, p;
4092   PetscInt numRoots = 0, numLeaves = 0;
4093 
4094   PetscFunctionBegin;
4095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4096   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4097 
4098   /* Create depth label */
4099   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4100   PetscCall(DMCreateLabel(dm, "depth"));
4101   PetscCall(DMPlexGetDepthLabel(dm, &label));
4102 
4103   {
4104     /* Initialize roots and count leaves */
4105     PetscInt sMin = PETSC_MAX_INT;
4106     PetscInt sMax = PETSC_MIN_INT;
4107     PetscInt coneSize, supportSize;
4108 
4109     for (p = pStart; p < pEnd; ++p) {
4110       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4111       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4112       if (!coneSize && supportSize) {
4113         sMin = PetscMin(p, sMin);
4114         sMax = PetscMax(p, sMax);
4115         ++numRoots;
4116       } else if (!supportSize && coneSize) {
4117         ++numLeaves;
4118       } else if (!supportSize && !coneSize) {
4119         /* Isolated points */
4120         sMin = PetscMin(p, sMin);
4121         sMax = PetscMax(p, sMax);
4122       }
4123     }
4124     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4125   }
4126 
4127   if (numRoots + numLeaves == (pEnd - pStart)) {
4128     PetscInt sMin = PETSC_MAX_INT;
4129     PetscInt sMax = PETSC_MIN_INT;
4130     PetscInt coneSize, supportSize;
4131 
4132     for (p = pStart; p < pEnd; ++p) {
4133       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4134       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4135       if (!supportSize && coneSize) {
4136         sMin = PetscMin(p, sMin);
4137         sMax = PetscMax(p, sMax);
4138       }
4139     }
4140     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4141   } else {
4142     PetscInt level = 0;
4143     PetscInt qStart, qEnd, q;
4144 
4145     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4146     while (qEnd > qStart) {
4147       PetscInt sMin = PETSC_MAX_INT;
4148       PetscInt sMax = PETSC_MIN_INT;
4149 
4150       for (q = qStart; q < qEnd; ++q) {
4151         const PetscInt *support;
4152         PetscInt        supportSize, s;
4153 
4154         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4155         PetscCall(DMPlexGetSupport(dm, q, &support));
4156         for (s = 0; s < supportSize; ++s) {
4157           sMin = PetscMin(support[s], sMin);
4158           sMax = PetscMax(support[s], sMax);
4159         }
4160       }
4161       PetscCall(DMLabelGetNumValues(label, &level));
4162       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4163       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4164     }
4165   }
4166   { /* just in case there is an empty process */
4167     PetscInt numValues, maxValues = 0, v;
4168 
4169     PetscCall(DMLabelGetNumValues(label, &numValues));
4170     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4171     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4172   }
4173   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4174   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4175   PetscFunctionReturn(PETSC_SUCCESS);
4176 }
4177 
4178 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4179 {
4180   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4181   PetscInt       dim, depth, pheight, coneSize;
4182 
4183   PetscFunctionBeginHot;
4184   PetscCall(DMGetDimension(dm, &dim));
4185   PetscCall(DMPlexGetDepth(dm, &depth));
4186   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4187   pheight = depth - pdepth;
4188   if (depth <= 1) {
4189     switch (pdepth) {
4190     case 0:
4191       ct = DM_POLYTOPE_POINT;
4192       break;
4193     case 1:
4194       switch (coneSize) {
4195       case 2:
4196         ct = DM_POLYTOPE_SEGMENT;
4197         break;
4198       case 3:
4199         ct = DM_POLYTOPE_TRIANGLE;
4200         break;
4201       case 4:
4202         switch (dim) {
4203         case 2:
4204           ct = DM_POLYTOPE_QUADRILATERAL;
4205           break;
4206         case 3:
4207           ct = DM_POLYTOPE_TETRAHEDRON;
4208           break;
4209         default:
4210           break;
4211         }
4212         break;
4213       case 5:
4214         ct = DM_POLYTOPE_PYRAMID;
4215         break;
4216       case 6:
4217         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4218         break;
4219       case 8:
4220         ct = DM_POLYTOPE_HEXAHEDRON;
4221         break;
4222       default:
4223         break;
4224       }
4225     }
4226   } else {
4227     if (pdepth == 0) {
4228       ct = DM_POLYTOPE_POINT;
4229     } else if (pheight == 0) {
4230       switch (dim) {
4231       case 1:
4232         switch (coneSize) {
4233         case 2:
4234           ct = DM_POLYTOPE_SEGMENT;
4235           break;
4236         default:
4237           break;
4238         }
4239         break;
4240       case 2:
4241         switch (coneSize) {
4242         case 3:
4243           ct = DM_POLYTOPE_TRIANGLE;
4244           break;
4245         case 4:
4246           ct = DM_POLYTOPE_QUADRILATERAL;
4247           break;
4248         default:
4249           break;
4250         }
4251         break;
4252       case 3:
4253         switch (coneSize) {
4254         case 4:
4255           ct = DM_POLYTOPE_TETRAHEDRON;
4256           break;
4257         case 5: {
4258           const PetscInt *cone;
4259           PetscInt        faceConeSize;
4260 
4261           PetscCall(DMPlexGetCone(dm, p, &cone));
4262           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4263           switch (faceConeSize) {
4264           case 3:
4265             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4266             break;
4267           case 4:
4268             ct = DM_POLYTOPE_PYRAMID;
4269             break;
4270           }
4271         } break;
4272         case 6:
4273           ct = DM_POLYTOPE_HEXAHEDRON;
4274           break;
4275         default:
4276           break;
4277         }
4278         break;
4279       default:
4280         break;
4281       }
4282     } else if (pheight > 0) {
4283       switch (coneSize) {
4284       case 2:
4285         ct = DM_POLYTOPE_SEGMENT;
4286         break;
4287       case 3:
4288         ct = DM_POLYTOPE_TRIANGLE;
4289         break;
4290       case 4:
4291         ct = DM_POLYTOPE_QUADRILATERAL;
4292         break;
4293       default:
4294         break;
4295       }
4296     }
4297   }
4298   *pt = ct;
4299   PetscFunctionReturn(PETSC_SUCCESS);
4300 }
4301 
4302 /*@
4303   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4304 
4305   Collective
4306 
4307   Input Parameter:
4308 . mesh - The `DMPLEX`
4309 
4310   Level: developer
4311 
4312   Note:
4313   This function is normally called automatically when a cell type is requested. It creates an
4314   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4315   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4316 
4317   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4318 
4319 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4320 @*/
4321 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4322 {
4323   DM_Plex *mesh;
4324   DMLabel  ctLabel;
4325   PetscInt pStart, pEnd, p;
4326 
4327   PetscFunctionBegin;
4328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4329   mesh = (DM_Plex *)dm->data;
4330   PetscCall(DMCreateLabel(dm, "celltype"));
4331   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4332   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4333   for (p = pStart; p < pEnd; ++p) {
4334     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4335     PetscInt       pdepth;
4336 
4337     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4338     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4339     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4340     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4341   }
4342   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4343   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4344   PetscFunctionReturn(PETSC_SUCCESS);
4345 }
4346 
4347 /*@C
4348   DMPlexGetJoin - Get an array for the join of the set of points
4349 
4350   Not Collective
4351 
4352   Input Parameters:
4353 + dm - The `DMPLEX` object
4354 . numPoints - The number of input points for the join
4355 - points - The input points
4356 
4357   Output Parameters:
4358 + numCoveredPoints - The number of points in the join
4359 - coveredPoints - The points in the join
4360 
4361   Level: intermediate
4362 
4363   Note:
4364   Currently, this is restricted to a single level join
4365 
4366   Fortran Note:
4367   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4368 
4369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4370 @*/
4371 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4372 {
4373   DM_Plex  *mesh = (DM_Plex *)dm->data;
4374   PetscInt *join[2];
4375   PetscInt  joinSize, i = 0;
4376   PetscInt  dof, off, p, c, m;
4377   PetscInt  maxSupportSize;
4378 
4379   PetscFunctionBegin;
4380   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4381   PetscValidIntPointer(points, 3);
4382   PetscValidIntPointer(numCoveredPoints, 4);
4383   PetscValidPointer(coveredPoints, 5);
4384   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4385   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4386   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4387   /* Copy in support of first point */
4388   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4389   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4390   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4391   /* Check each successive support */
4392   for (p = 1; p < numPoints; ++p) {
4393     PetscInt newJoinSize = 0;
4394 
4395     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4396     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4397     for (c = 0; c < dof; ++c) {
4398       const PetscInt point = mesh->supports[off + c];
4399 
4400       for (m = 0; m < joinSize; ++m) {
4401         if (point == join[i][m]) {
4402           join[1 - i][newJoinSize++] = point;
4403           break;
4404         }
4405       }
4406     }
4407     joinSize = newJoinSize;
4408     i        = 1 - i;
4409   }
4410   *numCoveredPoints = joinSize;
4411   *coveredPoints    = join[i];
4412   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4413   PetscFunctionReturn(PETSC_SUCCESS);
4414 }
4415 
4416 /*@C
4417   DMPlexRestoreJoin - Restore an array for the join of the set of points
4418 
4419   Not Collective
4420 
4421   Input Parameters:
4422 + dm - The `DMPLEX` object
4423 . numPoints - The number of input points for the join
4424 - points - The input points
4425 
4426   Output Parameters:
4427 + numCoveredPoints - The number of points in the join
4428 - coveredPoints - The points in the join
4429 
4430   Level: intermediate
4431 
4432   Fortran Note:
4433   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4434 
4435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4436 @*/
4437 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4438 {
4439   PetscFunctionBegin;
4440   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4441   if (points) PetscValidIntPointer(points, 3);
4442   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4443   PetscValidPointer(coveredPoints, 5);
4444   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4445   if (numCoveredPoints) *numCoveredPoints = 0;
4446   PetscFunctionReturn(PETSC_SUCCESS);
4447 }
4448 
4449 /*@C
4450   DMPlexGetFullJoin - Get an array for the join of the set of points
4451 
4452   Not Collective
4453 
4454   Input Parameters:
4455 + dm - The `DMPLEX` object
4456 . numPoints - The number of input points for the join
4457 - points - The input points
4458 
4459   Output Parameters:
4460 + numCoveredPoints - The number of points in the join
4461 - coveredPoints - The points in the join
4462 
4463   Level: intermediate
4464 
4465   Fortran Note:
4466   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4467 
4468 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4469 @*/
4470 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4471 {
4472   PetscInt *offsets, **closures;
4473   PetscInt *join[2];
4474   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4475   PetscInt  p, d, c, m, ms;
4476 
4477   PetscFunctionBegin;
4478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4479   PetscValidIntPointer(points, 3);
4480   PetscValidIntPointer(numCoveredPoints, 4);
4481   PetscValidPointer(coveredPoints, 5);
4482 
4483   PetscCall(DMPlexGetDepth(dm, &depth));
4484   PetscCall(PetscCalloc1(numPoints, &closures));
4485   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4486   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4487   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4488   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4489   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4490 
4491   for (p = 0; p < numPoints; ++p) {
4492     PetscInt closureSize;
4493 
4494     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4495 
4496     offsets[p * (depth + 2) + 0] = 0;
4497     for (d = 0; d < depth + 1; ++d) {
4498       PetscInt pStart, pEnd, i;
4499 
4500       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4501       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4502         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4503           offsets[p * (depth + 2) + d + 1] = i;
4504           break;
4505         }
4506       }
4507       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4508     }
4509     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);
4510   }
4511   for (d = 0; d < depth + 1; ++d) {
4512     PetscInt dof;
4513 
4514     /* Copy in support of first point */
4515     dof = offsets[d + 1] - offsets[d];
4516     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4517     /* Check each successive cone */
4518     for (p = 1; p < numPoints && joinSize; ++p) {
4519       PetscInt newJoinSize = 0;
4520 
4521       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4522       for (c = 0; c < dof; ++c) {
4523         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4524 
4525         for (m = 0; m < joinSize; ++m) {
4526           if (point == join[i][m]) {
4527             join[1 - i][newJoinSize++] = point;
4528             break;
4529           }
4530         }
4531       }
4532       joinSize = newJoinSize;
4533       i        = 1 - i;
4534     }
4535     if (joinSize) break;
4536   }
4537   *numCoveredPoints = joinSize;
4538   *coveredPoints    = join[i];
4539   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4540   PetscCall(PetscFree(closures));
4541   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4542   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4543   PetscFunctionReturn(PETSC_SUCCESS);
4544 }
4545 
4546 /*@C
4547   DMPlexGetMeet - Get an array for the meet of the set of points
4548 
4549   Not Collective
4550 
4551   Input Parameters:
4552 + dm - The `DMPLEX` object
4553 . numPoints - The number of input points for the meet
4554 - points - The input points
4555 
4556   Output Parameters:
4557 + numCoveredPoints - The number of points in the meet
4558 - coveredPoints - The points in the meet
4559 
4560   Level: intermediate
4561 
4562   Note:
4563   Currently, this is restricted to a single level meet
4564 
4565   Fortran Notes:
4566   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4567 
4568 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4569 @*/
4570 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4571 {
4572   DM_Plex  *mesh = (DM_Plex *)dm->data;
4573   PetscInt *meet[2];
4574   PetscInt  meetSize, i = 0;
4575   PetscInt  dof, off, p, c, m;
4576   PetscInt  maxConeSize;
4577 
4578   PetscFunctionBegin;
4579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4580   PetscValidIntPointer(points, 3);
4581   PetscValidIntPointer(numCoveringPoints, 4);
4582   PetscValidPointer(coveringPoints, 5);
4583   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4584   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4585   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4586   /* Copy in cone of first point */
4587   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4588   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4589   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4590   /* Check each successive cone */
4591   for (p = 1; p < numPoints; ++p) {
4592     PetscInt newMeetSize = 0;
4593 
4594     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4595     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4596     for (c = 0; c < dof; ++c) {
4597       const PetscInt point = mesh->cones[off + c];
4598 
4599       for (m = 0; m < meetSize; ++m) {
4600         if (point == meet[i][m]) {
4601           meet[1 - i][newMeetSize++] = point;
4602           break;
4603         }
4604       }
4605     }
4606     meetSize = newMeetSize;
4607     i        = 1 - i;
4608   }
4609   *numCoveringPoints = meetSize;
4610   *coveringPoints    = meet[i];
4611   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4612   PetscFunctionReturn(PETSC_SUCCESS);
4613 }
4614 
4615 /*@C
4616   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4617 
4618   Not Collective
4619 
4620   Input Parameters:
4621 + dm - The `DMPLEX` object
4622 . numPoints - The number of input points for the meet
4623 - points - The input points
4624 
4625   Output Parameters:
4626 + numCoveredPoints - The number of points in the meet
4627 - coveredPoints - The points in the meet
4628 
4629   Level: intermediate
4630 
4631   Fortran Note:
4632   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4633 
4634 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4635 @*/
4636 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4637 {
4638   PetscFunctionBegin;
4639   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4640   if (points) PetscValidIntPointer(points, 3);
4641   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4642   PetscValidPointer(coveredPoints, 5);
4643   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4644   if (numCoveredPoints) *numCoveredPoints = 0;
4645   PetscFunctionReturn(PETSC_SUCCESS);
4646 }
4647 
4648 /*@C
4649   DMPlexGetFullMeet - Get an array for the meet of the set of points
4650 
4651   Not Collective
4652 
4653   Input Parameters:
4654 + dm - The `DMPLEX` object
4655 . numPoints - The number of input points for the meet
4656 - points - The input points
4657 
4658   Output Parameters:
4659 + numCoveredPoints - The number of points in the meet
4660 - coveredPoints - The points in the meet
4661 
4662   Level: intermediate
4663 
4664   Fortran Note:
4665   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4666 
4667 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4668 @*/
4669 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4670 {
4671   PetscInt *offsets, **closures;
4672   PetscInt *meet[2];
4673   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4674   PetscInt  p, h, c, m, mc;
4675 
4676   PetscFunctionBegin;
4677   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4678   PetscValidIntPointer(points, 3);
4679   PetscValidIntPointer(numCoveredPoints, 4);
4680   PetscValidPointer(coveredPoints, 5);
4681 
4682   PetscCall(DMPlexGetDepth(dm, &height));
4683   PetscCall(PetscMalloc1(numPoints, &closures));
4684   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4685   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4686   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4687   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4688   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4689 
4690   for (p = 0; p < numPoints; ++p) {
4691     PetscInt closureSize;
4692 
4693     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4694 
4695     offsets[p * (height + 2) + 0] = 0;
4696     for (h = 0; h < height + 1; ++h) {
4697       PetscInt pStart, pEnd, i;
4698 
4699       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4700       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4701         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4702           offsets[p * (height + 2) + h + 1] = i;
4703           break;
4704         }
4705       }
4706       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4707     }
4708     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);
4709   }
4710   for (h = 0; h < height + 1; ++h) {
4711     PetscInt dof;
4712 
4713     /* Copy in cone of first point */
4714     dof = offsets[h + 1] - offsets[h];
4715     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4716     /* Check each successive cone */
4717     for (p = 1; p < numPoints && meetSize; ++p) {
4718       PetscInt newMeetSize = 0;
4719 
4720       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4721       for (c = 0; c < dof; ++c) {
4722         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4723 
4724         for (m = 0; m < meetSize; ++m) {
4725           if (point == meet[i][m]) {
4726             meet[1 - i][newMeetSize++] = point;
4727             break;
4728           }
4729         }
4730       }
4731       meetSize = newMeetSize;
4732       i        = 1 - i;
4733     }
4734     if (meetSize) break;
4735   }
4736   *numCoveredPoints = meetSize;
4737   *coveredPoints    = meet[i];
4738   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4739   PetscCall(PetscFree(closures));
4740   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4741   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4742   PetscFunctionReturn(PETSC_SUCCESS);
4743 }
4744 
4745 /*@C
4746   DMPlexEqual - Determine if two `DM` have the same topology
4747 
4748   Not Collective
4749 
4750   Input Parameters:
4751 + dmA - A `DMPLEX` object
4752 - dmB - A `DMPLEX` object
4753 
4754   Output Parameter:
4755 . equal - `PETSC_TRUE` if the topologies are identical
4756 
4757   Level: intermediate
4758 
4759   Note:
4760   We are not solving graph isomorphism, so we do not permute.
4761 
4762 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4763 @*/
4764 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4765 {
4766   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4767 
4768   PetscFunctionBegin;
4769   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4770   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4771   PetscValidBoolPointer(equal, 3);
4772 
4773   *equal = PETSC_FALSE;
4774   PetscCall(DMPlexGetDepth(dmA, &depth));
4775   PetscCall(DMPlexGetDepth(dmB, &depthB));
4776   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4777   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4778   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4779   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4780   for (p = pStart; p < pEnd; ++p) {
4781     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4782     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4783 
4784     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4785     PetscCall(DMPlexGetCone(dmA, p, &cone));
4786     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4787     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4788     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4789     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4790     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4791     for (c = 0; c < coneSize; ++c) {
4792       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4793       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4794     }
4795     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4796     PetscCall(DMPlexGetSupport(dmA, p, &support));
4797     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4798     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4799     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4800     for (s = 0; s < supportSize; ++s) {
4801       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4802     }
4803   }
4804   *equal = PETSC_TRUE;
4805   PetscFunctionReturn(PETSC_SUCCESS);
4806 }
4807 
4808 /*@C
4809   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4810 
4811   Not Collective
4812 
4813   Input Parameters:
4814 + dm         - The `DMPLEX`
4815 . cellDim    - The cell dimension
4816 - numCorners - The number of vertices on a cell
4817 
4818   Output Parameter:
4819 . numFaceVertices - The number of vertices on a face
4820 
4821   Level: developer
4822 
4823   Note:
4824   Of course this can only work for a restricted set of symmetric shapes
4825 
4826 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4827 @*/
4828 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4829 {
4830   MPI_Comm comm;
4831 
4832   PetscFunctionBegin;
4833   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4834   PetscValidIntPointer(numFaceVertices, 4);
4835   switch (cellDim) {
4836   case 0:
4837     *numFaceVertices = 0;
4838     break;
4839   case 1:
4840     *numFaceVertices = 1;
4841     break;
4842   case 2:
4843     switch (numCorners) {
4844     case 3:                 /* triangle */
4845       *numFaceVertices = 2; /* Edge has 2 vertices */
4846       break;
4847     case 4:                 /* quadrilateral */
4848       *numFaceVertices = 2; /* Edge has 2 vertices */
4849       break;
4850     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4851       *numFaceVertices = 3; /* Edge has 3 vertices */
4852       break;
4853     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4854       *numFaceVertices = 3; /* Edge has 3 vertices */
4855       break;
4856     default:
4857       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4858     }
4859     break;
4860   case 3:
4861     switch (numCorners) {
4862     case 4:                 /* tetradehdron */
4863       *numFaceVertices = 3; /* Face has 3 vertices */
4864       break;
4865     case 6:                 /* tet cohesive cells */
4866       *numFaceVertices = 4; /* Face has 4 vertices */
4867       break;
4868     case 8:                 /* hexahedron */
4869       *numFaceVertices = 4; /* Face has 4 vertices */
4870       break;
4871     case 9:                 /* tet cohesive Lagrange cells */
4872       *numFaceVertices = 6; /* Face has 6 vertices */
4873       break;
4874     case 10:                /* quadratic tetrahedron */
4875       *numFaceVertices = 6; /* Face has 6 vertices */
4876       break;
4877     case 12:                /* hex cohesive Lagrange cells */
4878       *numFaceVertices = 6; /* Face has 6 vertices */
4879       break;
4880     case 18:                /* quadratic tet cohesive Lagrange cells */
4881       *numFaceVertices = 6; /* Face has 6 vertices */
4882       break;
4883     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4884       *numFaceVertices = 9; /* Face has 9 vertices */
4885       break;
4886     default:
4887       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4888     }
4889     break;
4890   default:
4891     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4892   }
4893   PetscFunctionReturn(PETSC_SUCCESS);
4894 }
4895 
4896 /*@
4897   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4898 
4899   Not Collective
4900 
4901   Input Parameter:
4902 . dm    - The `DMPLEX` object
4903 
4904   Output Parameter:
4905 . depthLabel - The `DMLabel` recording point depth
4906 
4907   Level: developer
4908 
4909 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4910 @*/
4911 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4912 {
4913   PetscFunctionBegin;
4914   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4915   PetscValidPointer(depthLabel, 2);
4916   *depthLabel = dm->depthLabel;
4917   PetscFunctionReturn(PETSC_SUCCESS);
4918 }
4919 
4920 /*@
4921   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4922 
4923   Not Collective
4924 
4925   Input Parameter:
4926 . dm    - The `DMPLEX` object
4927 
4928   Output Parameter:
4929 . depth - The number of strata (breadth first levels) in the DAG
4930 
4931   Level: developer
4932 
4933   Notes:
4934   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4935 
4936   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4937 
4938   An empty mesh gives -1.
4939 
4940 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4941 @*/
4942 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4943 {
4944   DM_Plex *mesh = (DM_Plex *)dm->data;
4945   DMLabel  label;
4946   PetscInt d = 0;
4947 
4948   PetscFunctionBegin;
4949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4950   PetscValidIntPointer(depth, 2);
4951   if (mesh->tr) {
4952     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4953   } else {
4954     PetscCall(DMPlexGetDepthLabel(dm, &label));
4955     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4956     *depth = d - 1;
4957   }
4958   PetscFunctionReturn(PETSC_SUCCESS);
4959 }
4960 
4961 /*@
4962   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
4963 
4964   Not Collective
4965 
4966   Input Parameters:
4967 + dm    - The `DMPLEX` object
4968 - depth - The requested depth
4969 
4970   Output Parameters:
4971 + start - The first point at this `depth`
4972 - end   - One beyond the last point at this `depth`
4973 
4974   Level: developer
4975 
4976   Notes:
4977   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4978   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4979   higher dimension, e.g., "edges".
4980 
4981 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4982 @*/
4983 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4984 {
4985   DM_Plex *mesh = (DM_Plex *)dm->data;
4986   DMLabel  label;
4987   PetscInt pStart, pEnd;
4988 
4989   PetscFunctionBegin;
4990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4991   if (start) {
4992     PetscValidIntPointer(start, 3);
4993     *start = 0;
4994   }
4995   if (end) {
4996     PetscValidIntPointer(end, 4);
4997     *end = 0;
4998   }
4999   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5000   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5001   if (depth < 0) {
5002     if (start) *start = pStart;
5003     if (end) *end = pEnd;
5004     PetscFunctionReturn(PETSC_SUCCESS);
5005   }
5006   if (mesh->tr) {
5007     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5008   } else {
5009     PetscCall(DMPlexGetDepthLabel(dm, &label));
5010     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5011     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5012   }
5013   PetscFunctionReturn(PETSC_SUCCESS);
5014 }
5015 
5016 /*@
5017   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5018 
5019   Not Collective
5020 
5021   Input Parameters:
5022 + dm     - The `DMPLEX` object
5023 - height - The requested height
5024 
5025   Output Parameters:
5026 + start - The first point at this `height`
5027 - end   - One beyond the last point at this `height`
5028 
5029   Level: developer
5030 
5031   Notes:
5032   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5033   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5034   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5035 
5036 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5037 @*/
5038 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5039 {
5040   DMLabel  label;
5041   PetscInt depth, pStart, pEnd;
5042 
5043   PetscFunctionBegin;
5044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5045   if (start) {
5046     PetscValidIntPointer(start, 3);
5047     *start = 0;
5048   }
5049   if (end) {
5050     PetscValidIntPointer(end, 4);
5051     *end = 0;
5052   }
5053   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5054   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5055   if (height < 0) {
5056     if (start) *start = pStart;
5057     if (end) *end = pEnd;
5058     PetscFunctionReturn(PETSC_SUCCESS);
5059   }
5060   PetscCall(DMPlexGetDepthLabel(dm, &label));
5061   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5062   PetscCall(DMLabelGetNumValues(label, &depth));
5063   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5064   PetscFunctionReturn(PETSC_SUCCESS);
5065 }
5066 
5067 /*@
5068   DMPlexGetPointDepth - Get the `depth` of a given point
5069 
5070   Not Collective
5071 
5072   Input Parameters:
5073 + dm    - The `DMPLEX` object
5074 - point - The point
5075 
5076   Output Parameter:
5077 . depth - The depth of the `point`
5078 
5079   Level: intermediate
5080 
5081 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5082 @*/
5083 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5084 {
5085   PetscFunctionBegin;
5086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5087   PetscValidIntPointer(depth, 3);
5088   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5089   PetscFunctionReturn(PETSC_SUCCESS);
5090 }
5091 
5092 /*@
5093   DMPlexGetPointHeight - Get the `height` of a given point
5094 
5095   Not Collective
5096 
5097   Input Parameters:
5098 + dm    - The `DMPLEX` object
5099 - point - The point
5100 
5101   Output Parameter:
5102 . height - The height of the `point`
5103 
5104   Level: intermediate
5105 
5106 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5107 @*/
5108 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5109 {
5110   PetscInt n, pDepth;
5111 
5112   PetscFunctionBegin;
5113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5114   PetscValidIntPointer(height, 3);
5115   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5116   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5117   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5118   PetscFunctionReturn(PETSC_SUCCESS);
5119 }
5120 
5121 /*@
5122   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5123 
5124   Not Collective
5125 
5126   Input Parameter:
5127 . dm - The `DMPLEX` object
5128 
5129   Output Parameter:
5130 . celltypeLabel - The `DMLabel` recording cell polytope type
5131 
5132   Level: developer
5133 
5134   Note:
5135   This function will trigger automatica computation of cell types. This can be disabled by calling
5136   `DMCreateLabel`(dm, "celltype") beforehand.
5137 
5138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5139 @*/
5140 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5141 {
5142   PetscFunctionBegin;
5143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5144   PetscValidPointer(celltypeLabel, 2);
5145   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5146   *celltypeLabel = dm->celltypeLabel;
5147   PetscFunctionReturn(PETSC_SUCCESS);
5148 }
5149 
5150 /*@
5151   DMPlexGetCellType - Get the polytope type of a given cell
5152 
5153   Not Collective
5154 
5155   Input Parameters:
5156 + dm   - The `DMPLEX` object
5157 - cell - The cell
5158 
5159   Output Parameter:
5160 . celltype - The polytope type of the cell
5161 
5162   Level: intermediate
5163 
5164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5165 @*/
5166 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5167 {
5168   DM_Plex *mesh = (DM_Plex *)dm->data;
5169   DMLabel  label;
5170   PetscInt ct;
5171 
5172   PetscFunctionBegin;
5173   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5174   PetscValidPointer(celltype, 3);
5175   if (mesh->tr) {
5176     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5177   } else {
5178     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5179     PetscCall(DMLabelGetValue(label, cell, &ct));
5180     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5181     *celltype = (DMPolytopeType)ct;
5182   }
5183   PetscFunctionReturn(PETSC_SUCCESS);
5184 }
5185 
5186 /*@
5187   DMPlexSetCellType - Set the polytope type of a given cell
5188 
5189   Not Collective
5190 
5191   Input Parameters:
5192 + dm   - The `DMPLEX` object
5193 . cell - The cell
5194 - celltype - The polytope type of the cell
5195 
5196   Level: advanced
5197 
5198   Note:
5199   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5200   is executed. This function will override the computed type. However, if automatic classification will not succeed
5201   and a user wants to manually specify all types, the classification must be disabled by calling
5202   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5203 
5204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5205 @*/
5206 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5207 {
5208   DMLabel label;
5209 
5210   PetscFunctionBegin;
5211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5212   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5213   PetscCall(DMLabelSetValue(label, cell, celltype));
5214   PetscFunctionReturn(PETSC_SUCCESS);
5215 }
5216 
5217 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5218 {
5219   PetscSection section, s;
5220   Mat          m;
5221   PetscInt     maxHeight;
5222   const char  *prefix;
5223 
5224   PetscFunctionBegin;
5225   PetscCall(DMClone(dm, cdm));
5226   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5227   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5228   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5229   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5230   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5231   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5232   PetscCall(DMSetLocalSection(*cdm, section));
5233   PetscCall(PetscSectionDestroy(&section));
5234   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5235   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5236   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5237   PetscCall(PetscSectionDestroy(&s));
5238   PetscCall(MatDestroy(&m));
5239 
5240   PetscCall(DMSetNumFields(*cdm, 1));
5241   PetscCall(DMCreateDS(*cdm));
5242   (*cdm)->cloneOpts = PETSC_TRUE;
5243   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5244   PetscFunctionReturn(PETSC_SUCCESS);
5245 }
5246 
5247 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5248 {
5249   Vec coordsLocal, cellCoordsLocal;
5250   DM  coordsDM, cellCoordsDM;
5251 
5252   PetscFunctionBegin;
5253   *field = NULL;
5254   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5255   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5256   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5257   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5258   if (coordsLocal && coordsDM) {
5259     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5260     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5261   }
5262   PetscFunctionReturn(PETSC_SUCCESS);
5263 }
5264 
5265 /*@C
5266   DMPlexGetConeSection - Return a section which describes the layout of cone data
5267 
5268   Not Collective
5269 
5270   Input Parameter:
5271 . dm        - The `DMPLEX` object
5272 
5273   Output Parameter:
5274 . section - The `PetscSection` object
5275 
5276   Level: developer
5277 
5278 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5279 @*/
5280 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5281 {
5282   DM_Plex *mesh = (DM_Plex *)dm->data;
5283 
5284   PetscFunctionBegin;
5285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5286   if (section) *section = mesh->coneSection;
5287   PetscFunctionReturn(PETSC_SUCCESS);
5288 }
5289 
5290 /*@C
5291   DMPlexGetSupportSection - Return a section which describes the layout of support data
5292 
5293   Not Collective
5294 
5295   Input Parameter:
5296 . dm        - The `DMPLEX` object
5297 
5298   Output Parameter:
5299 . section - The `PetscSection` object
5300 
5301   Level: developer
5302 
5303 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5304 @*/
5305 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5306 {
5307   DM_Plex *mesh = (DM_Plex *)dm->data;
5308 
5309   PetscFunctionBegin;
5310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5311   if (section) *section = mesh->supportSection;
5312   PetscFunctionReturn(PETSC_SUCCESS);
5313 }
5314 
5315 /*@C
5316   DMPlexGetCones - Return cone data
5317 
5318   Not Collective
5319 
5320   Input Parameter:
5321 . dm        - The `DMPLEX` object
5322 
5323   Output Parameter:
5324 . cones - The cone for each point
5325 
5326   Level: developer
5327 
5328 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5329 @*/
5330 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5331 {
5332   DM_Plex *mesh = (DM_Plex *)dm->data;
5333 
5334   PetscFunctionBegin;
5335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5336   if (cones) *cones = mesh->cones;
5337   PetscFunctionReturn(PETSC_SUCCESS);
5338 }
5339 
5340 /*@C
5341   DMPlexGetConeOrientations - Return cone orientation data
5342 
5343   Not Collective
5344 
5345   Input Parameter:
5346 . dm        - The `DMPLEX` object
5347 
5348   Output Parameter:
5349 . coneOrientations - The array of cone orientations for all points
5350 
5351   Level: developer
5352 
5353   Notes:
5354   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5355 
5356   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5357 
5358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5359 @*/
5360 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5361 {
5362   DM_Plex *mesh = (DM_Plex *)dm->data;
5363 
5364   PetscFunctionBegin;
5365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5366   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5367   PetscFunctionReturn(PETSC_SUCCESS);
5368 }
5369 
5370 /******************************** FEM Support **********************************/
5371 
5372 /*
5373  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5374  representing a line in the section.
5375 */
5376 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5377 {
5378   PetscFunctionBeginHot;
5379   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5380   if (line < 0) {
5381     *k  = 0;
5382     *Nc = 0;
5383   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5384     *k = 1;
5385   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5386     /* An order k SEM disc has k-1 dofs on an edge */
5387     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5388     *k = *k / *Nc + 1;
5389   }
5390   PetscFunctionReturn(PETSC_SUCCESS);
5391 }
5392 
5393 /*@
5394 
5395   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5396   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5397   section provided (or the section of the `DM`).
5398 
5399   Input Parameters:
5400 + dm      - The `DM`
5401 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5402 - section - The `PetscSection` to reorder, or `NULL` for the default section
5403 
5404   Example:
5405   A typical interpolated single-quad mesh might order points as
5406 .vb
5407   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5408 
5409   v4 -- e6 -- v3
5410   |           |
5411   e7    c0    e8
5412   |           |
5413   v1 -- e5 -- v2
5414 .ve
5415 
5416   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5417   dofs in the order of points, e.g.,
5418 .vb
5419     c0 -> [0,1,2,3]
5420     v1 -> [4]
5421     ...
5422     e5 -> [8, 9]
5423 .ve
5424 
5425   which corresponds to the dofs
5426 .vb
5427     6   10  11  7
5428     13  2   3   15
5429     12  0   1   14
5430     4   8   9   5
5431 .ve
5432 
5433   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5434 .vb
5435   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5436 .ve
5437 
5438   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5439 .vb
5440    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5441 .ve
5442 
5443   Level: developer
5444 
5445   Note:
5446   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5447   degree of the basis.
5448 
5449 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5450 @*/
5451 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5452 {
5453   DMLabel   label;
5454   PetscInt  dim, depth = -1, eStart = -1, Nf;
5455   PetscBool vertexchart;
5456 
5457   PetscFunctionBegin;
5458   PetscCall(DMGetDimension(dm, &dim));
5459   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5460   if (point < 0) {
5461     PetscInt sStart, sEnd;
5462 
5463     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5464     point = sEnd - sStart ? sStart : point;
5465   }
5466   PetscCall(DMPlexGetDepthLabel(dm, &label));
5467   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5468   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5469   if (depth == 1) {
5470     eStart = point;
5471   } else if (depth == dim) {
5472     const PetscInt *cone;
5473 
5474     PetscCall(DMPlexGetCone(dm, point, &cone));
5475     if (dim == 2) eStart = cone[0];
5476     else if (dim == 3) {
5477       const PetscInt *cone2;
5478       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5479       eStart = cone2[0];
5480     } 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);
5481   } 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);
5482   { /* Determine whether the chart covers all points or just vertices. */
5483     PetscInt pStart, pEnd, cStart, cEnd;
5484     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5485     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5486     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5487     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5488     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5489   }
5490   PetscCall(PetscSectionGetNumFields(section, &Nf));
5491   for (PetscInt d = 1; d <= dim; d++) {
5492     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5493     PetscInt *perm;
5494 
5495     for (f = 0; f < Nf; ++f) {
5496       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5497       size += PetscPowInt(k + 1, d) * Nc;
5498     }
5499     PetscCall(PetscMalloc1(size, &perm));
5500     for (f = 0; f < Nf; ++f) {
5501       switch (d) {
5502       case 1:
5503         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5504         /*
5505          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5506          We want              [ vtx0; edge of length k-1; vtx1 ]
5507          */
5508         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5509         for (i = 0; i < k - 1; i++)
5510           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5511         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5512         foffset = offset;
5513         break;
5514       case 2:
5515         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5516         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5517         /* The SEM order is
5518 
5519          v_lb, {e_b}, v_rb,
5520          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5521          v_lt, reverse {e_t}, v_rt
5522          */
5523         {
5524           const PetscInt of   = 0;
5525           const PetscInt oeb  = of + PetscSqr(k - 1);
5526           const PetscInt oer  = oeb + (k - 1);
5527           const PetscInt oet  = oer + (k - 1);
5528           const PetscInt oel  = oet + (k - 1);
5529           const PetscInt ovlb = oel + (k - 1);
5530           const PetscInt ovrb = ovlb + 1;
5531           const PetscInt ovrt = ovrb + 1;
5532           const PetscInt ovlt = ovrt + 1;
5533           PetscInt       o;
5534 
5535           /* bottom */
5536           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5537           for (o = oeb; o < oer; ++o)
5538             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5539           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5540           /* middle */
5541           for (i = 0; i < k - 1; ++i) {
5542             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5543             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5544               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5545             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5546           }
5547           /* top */
5548           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5549           for (o = oel - 1; o >= oet; --o)
5550             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5551           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5552           foffset = offset;
5553         }
5554         break;
5555       case 3:
5556         /* The original hex closure is
5557 
5558          {c,
5559          f_b, f_t, f_f, f_b, f_r, f_l,
5560          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5561          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5562          */
5563         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5564         /* The SEM order is
5565          Bottom Slice
5566          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5567          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5568          v_blb, {e_bb}, v_brb,
5569 
5570          Middle Slice (j)
5571          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5572          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5573          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5574 
5575          Top Slice
5576          v_tlf, {e_tf}, v_trf,
5577          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5578          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5579          */
5580         {
5581           const PetscInt oc    = 0;
5582           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5583           const PetscInt oft   = ofb + PetscSqr(k - 1);
5584           const PetscInt off   = oft + PetscSqr(k - 1);
5585           const PetscInt ofk   = off + PetscSqr(k - 1);
5586           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5587           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5588           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5589           const PetscInt oebb  = oebl + (k - 1);
5590           const PetscInt oebr  = oebb + (k - 1);
5591           const PetscInt oebf  = oebr + (k - 1);
5592           const PetscInt oetf  = oebf + (k - 1);
5593           const PetscInt oetr  = oetf + (k - 1);
5594           const PetscInt oetb  = oetr + (k - 1);
5595           const PetscInt oetl  = oetb + (k - 1);
5596           const PetscInt oerf  = oetl + (k - 1);
5597           const PetscInt oelf  = oerf + (k - 1);
5598           const PetscInt oelb  = oelf + (k - 1);
5599           const PetscInt oerb  = oelb + (k - 1);
5600           const PetscInt ovblf = oerb + (k - 1);
5601           const PetscInt ovblb = ovblf + 1;
5602           const PetscInt ovbrb = ovblb + 1;
5603           const PetscInt ovbrf = ovbrb + 1;
5604           const PetscInt ovtlf = ovbrf + 1;
5605           const PetscInt ovtrf = ovtlf + 1;
5606           const PetscInt ovtrb = ovtrf + 1;
5607           const PetscInt ovtlb = ovtrb + 1;
5608           PetscInt       o, n;
5609 
5610           /* Bottom Slice */
5611           /*   bottom */
5612           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5613           for (o = oetf - 1; o >= oebf; --o)
5614             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5615           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5616           /*   middle */
5617           for (i = 0; i < k - 1; ++i) {
5618             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5619             for (n = 0; n < k - 1; ++n) {
5620               o = ofb + n * (k - 1) + i;
5621               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5622             }
5623             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5624           }
5625           /*   top */
5626           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5627           for (o = oebb; o < oebr; ++o)
5628             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5629           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5630 
5631           /* Middle Slice */
5632           for (j = 0; j < k - 1; ++j) {
5633             /*   bottom */
5634             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5635             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5636               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5637             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5638             /*   middle */
5639             for (i = 0; i < k - 1; ++i) {
5640               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5641               for (n = 0; n < k - 1; ++n)
5642                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5643               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5644             }
5645             /*   top */
5646             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5647             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5648               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5649             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5650           }
5651 
5652           /* Top Slice */
5653           /*   bottom */
5654           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5655           for (o = oetf; o < oetr; ++o)
5656             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5657           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5658           /*   middle */
5659           for (i = 0; i < k - 1; ++i) {
5660             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5661             for (n = 0; n < k - 1; ++n)
5662               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5663             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5664           }
5665           /*   top */
5666           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5667           for (o = oetl - 1; o >= oetb; --o)
5668             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5669           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5670 
5671           foffset = offset;
5672         }
5673         break;
5674       default:
5675         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5676       }
5677     }
5678     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5679     /* Check permutation */
5680     {
5681       PetscInt *check;
5682 
5683       PetscCall(PetscMalloc1(size, &check));
5684       for (i = 0; i < size; ++i) {
5685         check[i] = -1;
5686         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5687       }
5688       for (i = 0; i < size; ++i) check[perm[i]] = i;
5689       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5690       PetscCall(PetscFree(check));
5691     }
5692     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5693     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5694       PetscInt *loc_perm;
5695       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5696       for (PetscInt i = 0; i < size; i++) {
5697         loc_perm[i]        = perm[i];
5698         loc_perm[size + i] = size + perm[i];
5699       }
5700       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5701     }
5702   }
5703   PetscFunctionReturn(PETSC_SUCCESS);
5704 }
5705 
5706 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5707 {
5708   PetscDS  prob;
5709   PetscInt depth, Nf, h;
5710   DMLabel  label;
5711 
5712   PetscFunctionBeginHot;
5713   PetscCall(DMGetDS(dm, &prob));
5714   Nf      = prob->Nf;
5715   label   = dm->depthLabel;
5716   *dspace = NULL;
5717   if (field < Nf) {
5718     PetscObject disc = prob->disc[field];
5719 
5720     if (disc->classid == PETSCFE_CLASSID) {
5721       PetscDualSpace dsp;
5722 
5723       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5724       PetscCall(DMLabelGetNumValues(label, &depth));
5725       PetscCall(DMLabelGetValue(label, point, &h));
5726       h = depth - 1 - h;
5727       if (h) {
5728         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5729       } else {
5730         *dspace = dsp;
5731       }
5732     }
5733   }
5734   PetscFunctionReturn(PETSC_SUCCESS);
5735 }
5736 
5737 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5738 {
5739   PetscScalar       *array;
5740   const PetscScalar *vArray;
5741   const PetscInt    *cone, *coneO;
5742   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5743 
5744   PetscFunctionBeginHot;
5745   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5746   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5747   PetscCall(DMPlexGetCone(dm, point, &cone));
5748   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5749   if (!values || !*values) {
5750     if ((point >= pStart) && (point < pEnd)) {
5751       PetscInt dof;
5752 
5753       PetscCall(PetscSectionGetDof(section, point, &dof));
5754       size += dof;
5755     }
5756     for (p = 0; p < numPoints; ++p) {
5757       const PetscInt cp = cone[p];
5758       PetscInt       dof;
5759 
5760       if ((cp < pStart) || (cp >= pEnd)) continue;
5761       PetscCall(PetscSectionGetDof(section, cp, &dof));
5762       size += dof;
5763     }
5764     if (!values) {
5765       if (csize) *csize = size;
5766       PetscFunctionReturn(PETSC_SUCCESS);
5767     }
5768     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5769   } else {
5770     array = *values;
5771   }
5772   size = 0;
5773   PetscCall(VecGetArrayRead(v, &vArray));
5774   if ((point >= pStart) && (point < pEnd)) {
5775     PetscInt           dof, off, d;
5776     const PetscScalar *varr;
5777 
5778     PetscCall(PetscSectionGetDof(section, point, &dof));
5779     PetscCall(PetscSectionGetOffset(section, point, &off));
5780     varr = &vArray[off];
5781     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5782     size += dof;
5783   }
5784   for (p = 0; p < numPoints; ++p) {
5785     const PetscInt     cp = cone[p];
5786     PetscInt           o  = coneO[p];
5787     PetscInt           dof, off, d;
5788     const PetscScalar *varr;
5789 
5790     if ((cp < pStart) || (cp >= pEnd)) continue;
5791     PetscCall(PetscSectionGetDof(section, cp, &dof));
5792     PetscCall(PetscSectionGetOffset(section, cp, &off));
5793     varr = &vArray[off];
5794     if (o >= 0) {
5795       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5796     } else {
5797       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5798     }
5799     size += dof;
5800   }
5801   PetscCall(VecRestoreArrayRead(v, &vArray));
5802   if (!*values) {
5803     if (csize) *csize = size;
5804     *values = array;
5805   } else {
5806     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5807     *csize = size;
5808   }
5809   PetscFunctionReturn(PETSC_SUCCESS);
5810 }
5811 
5812 /* Compress out points not in the section */
5813 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5814 {
5815   const PetscInt np = *numPoints;
5816   PetscInt       pStart, pEnd, p, q;
5817 
5818   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5819   for (p = 0, q = 0; p < np; ++p) {
5820     const PetscInt r = points[p * 2];
5821     if ((r >= pStart) && (r < pEnd)) {
5822       points[q * 2]     = r;
5823       points[q * 2 + 1] = points[p * 2 + 1];
5824       ++q;
5825     }
5826   }
5827   *numPoints = q;
5828   return PETSC_SUCCESS;
5829 }
5830 
5831 /* Compressed closure does not apply closure permutation */
5832 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5833 {
5834   const PetscInt *cla = NULL;
5835   PetscInt        np, *pts = NULL;
5836 
5837   PetscFunctionBeginHot;
5838   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5839   if (!ornt && *clPoints) {
5840     PetscInt dof, off;
5841 
5842     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5843     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5844     PetscCall(ISGetIndices(*clPoints, &cla));
5845     np  = dof / 2;
5846     pts = (PetscInt *)&cla[off];
5847   } else {
5848     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5849     PetscCall(CompressPoints_Private(section, &np, pts));
5850   }
5851   *numPoints = np;
5852   *points    = pts;
5853   *clp       = cla;
5854   PetscFunctionReturn(PETSC_SUCCESS);
5855 }
5856 
5857 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5858 {
5859   PetscFunctionBeginHot;
5860   if (!*clPoints) {
5861     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5862   } else {
5863     PetscCall(ISRestoreIndices(*clPoints, clp));
5864   }
5865   *numPoints = 0;
5866   *points    = NULL;
5867   *clSec     = NULL;
5868   *clPoints  = NULL;
5869   *clp       = NULL;
5870   PetscFunctionReturn(PETSC_SUCCESS);
5871 }
5872 
5873 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5874 {
5875   PetscInt            offset = 0, p;
5876   const PetscInt    **perms  = NULL;
5877   const PetscScalar **flips  = NULL;
5878 
5879   PetscFunctionBeginHot;
5880   *size = 0;
5881   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5882   for (p = 0; p < numPoints; p++) {
5883     const PetscInt     point = points[2 * p];
5884     const PetscInt    *perm  = perms ? perms[p] : NULL;
5885     const PetscScalar *flip  = flips ? flips[p] : NULL;
5886     PetscInt           dof, off, d;
5887     const PetscScalar *varr;
5888 
5889     PetscCall(PetscSectionGetDof(section, point, &dof));
5890     PetscCall(PetscSectionGetOffset(section, point, &off));
5891     varr = &vArray[off];
5892     if (clperm) {
5893       if (perm) {
5894         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5895       } else {
5896         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5897       }
5898       if (flip) {
5899         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5900       }
5901     } else {
5902       if (perm) {
5903         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5904       } else {
5905         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5906       }
5907       if (flip) {
5908         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5909       }
5910     }
5911     offset += dof;
5912   }
5913   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5914   *size = offset;
5915   PetscFunctionReturn(PETSC_SUCCESS);
5916 }
5917 
5918 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[])
5919 {
5920   PetscInt offset = 0, f;
5921 
5922   PetscFunctionBeginHot;
5923   *size = 0;
5924   for (f = 0; f < numFields; ++f) {
5925     PetscInt            p;
5926     const PetscInt    **perms = NULL;
5927     const PetscScalar **flips = NULL;
5928 
5929     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5930     for (p = 0; p < numPoints; p++) {
5931       const PetscInt     point = points[2 * p];
5932       PetscInt           fdof, foff, b;
5933       const PetscScalar *varr;
5934       const PetscInt    *perm = perms ? perms[p] : NULL;
5935       const PetscScalar *flip = flips ? flips[p] : NULL;
5936 
5937       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5938       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5939       varr = &vArray[foff];
5940       if (clperm) {
5941         if (perm) {
5942           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5943         } else {
5944           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5945         }
5946         if (flip) {
5947           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5948         }
5949       } else {
5950         if (perm) {
5951           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5952         } else {
5953           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5954         }
5955         if (flip) {
5956           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5957         }
5958       }
5959       offset += fdof;
5960     }
5961     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5962   }
5963   *size = offset;
5964   PetscFunctionReturn(PETSC_SUCCESS);
5965 }
5966 
5967 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
5968 {
5969   PetscSection    clSection;
5970   IS              clPoints;
5971   PetscInt       *points = NULL;
5972   const PetscInt *clp, *perm;
5973   PetscInt        depth, numFields, numPoints, asize;
5974 
5975   PetscFunctionBeginHot;
5976   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5977   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5978   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5979   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5980   PetscCall(DMPlexGetDepth(dm, &depth));
5981   PetscCall(PetscSectionGetNumFields(section, &numFields));
5982   if (depth == 1 && numFields < 2) {
5983     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5984     PetscFunctionReturn(PETSC_SUCCESS);
5985   }
5986   /* Get points */
5987   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
5988   /* Get sizes */
5989   asize = 0;
5990   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5991     PetscInt dof;
5992     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5993     asize += dof;
5994   }
5995   if (values) {
5996     const PetscScalar *vArray;
5997     PetscInt           size;
5998 
5999     if (*values) {
6000       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);
6001     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6002     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6003     PetscCall(VecGetArrayRead(v, &vArray));
6004     /* Get values */
6005     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6006     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6007     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6008     /* Cleanup array */
6009     PetscCall(VecRestoreArrayRead(v, &vArray));
6010   }
6011   if (csize) *csize = asize;
6012   /* Cleanup points */
6013   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6014   PetscFunctionReturn(PETSC_SUCCESS);
6015 }
6016 
6017 /*@C
6018   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6019 
6020   Not collective
6021 
6022   Input Parameters:
6023 + dm - The `DM`
6024 . section - The section describing the layout in `v`, or `NULL` to use the default section
6025 . v - The local vector
6026 - point - The point in the `DM`
6027 
6028   Input/Output Parameters:
6029 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6030 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6031            if the user provided `NULL`, it is a borrowed array and should not be freed
6032 
6033   Level: intermediate
6034 
6035   Notes:
6036   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6037   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6038   assembly function, and a user may already have allocated storage for this operation.
6039 
6040   A typical use could be
6041 .vb
6042    values = NULL;
6043    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6044    for (cl = 0; cl < clSize; ++cl) {
6045      <Compute on closure>
6046    }
6047    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6048 .ve
6049   or
6050 .vb
6051    PetscMalloc1(clMaxSize, &values);
6052    for (p = pStart; p < pEnd; ++p) {
6053      clSize = clMaxSize;
6054      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6055      for (cl = 0; cl < clSize; ++cl) {
6056        <Compute on closure>
6057      }
6058    }
6059    PetscFree(values);
6060 .ve
6061 
6062   Fortran Note:
6063   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6064 
6065 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6066 @*/
6067 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6068 {
6069   PetscFunctionBeginHot;
6070   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6071   PetscFunctionReturn(PETSC_SUCCESS);
6072 }
6073 
6074 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6075 {
6076   DMLabel            depthLabel;
6077   PetscSection       clSection;
6078   IS                 clPoints;
6079   PetscScalar       *array;
6080   const PetscScalar *vArray;
6081   PetscInt          *points = NULL;
6082   const PetscInt    *clp, *perm = NULL;
6083   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6084 
6085   PetscFunctionBeginHot;
6086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6087   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6088   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6089   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6090   PetscCall(DMPlexGetDepth(dm, &mdepth));
6091   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6092   PetscCall(PetscSectionGetNumFields(section, &numFields));
6093   if (mdepth == 1 && numFields < 2) {
6094     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6095     PetscFunctionReturn(PETSC_SUCCESS);
6096   }
6097   /* Get points */
6098   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6099   for (clsize = 0, p = 0; p < Np; p++) {
6100     PetscInt dof;
6101     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6102     clsize += dof;
6103   }
6104   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6105   /* Filter points */
6106   for (p = 0; p < numPoints * 2; p += 2) {
6107     PetscInt dep;
6108 
6109     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6110     if (dep != depth) continue;
6111     points[Np * 2 + 0] = points[p];
6112     points[Np * 2 + 1] = points[p + 1];
6113     ++Np;
6114   }
6115   /* Get array */
6116   if (!values || !*values) {
6117     PetscInt asize = 0, dof;
6118 
6119     for (p = 0; p < Np * 2; p += 2) {
6120       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6121       asize += dof;
6122     }
6123     if (!values) {
6124       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6125       if (csize) *csize = asize;
6126       PetscFunctionReturn(PETSC_SUCCESS);
6127     }
6128     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6129   } else {
6130     array = *values;
6131   }
6132   PetscCall(VecGetArrayRead(v, &vArray));
6133   /* Get values */
6134   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6135   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6136   /* Cleanup points */
6137   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6138   /* Cleanup array */
6139   PetscCall(VecRestoreArrayRead(v, &vArray));
6140   if (!*values) {
6141     if (csize) *csize = size;
6142     *values = array;
6143   } else {
6144     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6145     *csize = size;
6146   }
6147   PetscFunctionReturn(PETSC_SUCCESS);
6148 }
6149 
6150 /*@C
6151   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6152 
6153   Not collective
6154 
6155   Input Parameters:
6156 + dm - The `DM`
6157 . section - The section describing the layout in `v`, or `NULL` to use the default section
6158 . v - The local vector
6159 . point - The point in the `DM`
6160 . csize - The number of values in the closure, or `NULL`
6161 - values - The array of values, which is a borrowed array and should not be freed
6162 
6163   Level: intermediate
6164 
6165   Note:
6166   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6167 
6168   Fortran Note:
6169   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6170 
6171 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6172 @*/
6173 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6174 {
6175   PetscInt size = 0;
6176 
6177   PetscFunctionBegin;
6178   /* Should work without recalculating size */
6179   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6180   *values = NULL;
6181   PetscFunctionReturn(PETSC_SUCCESS);
6182 }
6183 
6184 static inline void add(PetscScalar *x, PetscScalar y)
6185 {
6186   *x += y;
6187 }
6188 static inline void insert(PetscScalar *x, PetscScalar y)
6189 {
6190   *x = y;
6191 }
6192 
6193 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[])
6194 {
6195   PetscInt        cdof;  /* The number of constraints on this point */
6196   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6197   PetscScalar    *a;
6198   PetscInt        off, cind = 0, k;
6199 
6200   PetscFunctionBegin;
6201   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6202   PetscCall(PetscSectionGetOffset(section, point, &off));
6203   a = &array[off];
6204   if (!cdof || setBC) {
6205     if (clperm) {
6206       if (perm) {
6207         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6208       } else {
6209         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6210       }
6211     } else {
6212       if (perm) {
6213         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6214       } else {
6215         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6216       }
6217     }
6218   } else {
6219     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6220     if (clperm) {
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[clperm[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[clperm[offset + k]] * (flip ? flip[k] : 1.));
6236         }
6237       }
6238     } else {
6239       if (perm) {
6240         for (k = 0; k < dof; ++k) {
6241           if ((cind < cdof) && (k == cdofs[cind])) {
6242             ++cind;
6243             continue;
6244           }
6245           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6246         }
6247       } else {
6248         for (k = 0; k < dof; ++k) {
6249           if ((cind < cdof) && (k == cdofs[cind])) {
6250             ++cind;
6251             continue;
6252           }
6253           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6254         }
6255       }
6256     }
6257   }
6258   PetscFunctionReturn(PETSC_SUCCESS);
6259 }
6260 
6261 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[])
6262 {
6263   PetscInt        cdof;  /* The number of constraints on this point */
6264   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6265   PetscScalar    *a;
6266   PetscInt        off, cind = 0, k;
6267 
6268   PetscFunctionBegin;
6269   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6270   PetscCall(PetscSectionGetOffset(section, point, &off));
6271   a = &array[off];
6272   if (cdof) {
6273     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6274     if (clperm) {
6275       if (perm) {
6276         for (k = 0; k < dof; ++k) {
6277           if ((cind < cdof) && (k == cdofs[cind])) {
6278             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6279             cind++;
6280           }
6281         }
6282       } else {
6283         for (k = 0; k < dof; ++k) {
6284           if ((cind < cdof) && (k == cdofs[cind])) {
6285             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6286             cind++;
6287           }
6288         }
6289       }
6290     } else {
6291       if (perm) {
6292         for (k = 0; k < dof; ++k) {
6293           if ((cind < cdof) && (k == cdofs[cind])) {
6294             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6295             cind++;
6296           }
6297         }
6298       } else {
6299         for (k = 0; k < dof; ++k) {
6300           if ((cind < cdof) && (k == cdofs[cind])) {
6301             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6302             cind++;
6303           }
6304         }
6305       }
6306     }
6307   }
6308   PetscFunctionReturn(PETSC_SUCCESS);
6309 }
6310 
6311 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[])
6312 {
6313   PetscScalar    *a;
6314   PetscInt        fdof, foff, fcdof, foffset = *offset;
6315   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6316   PetscInt        cind = 0, b;
6317 
6318   PetscFunctionBegin;
6319   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6320   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6321   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6322   a = &array[foff];
6323   if (!fcdof || setBC) {
6324     if (clperm) {
6325       if (perm) {
6326         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6327       } else {
6328         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6329       }
6330     } else {
6331       if (perm) {
6332         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6333       } else {
6334         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6335       }
6336     }
6337   } else {
6338     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6339     if (clperm) {
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[clperm[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[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6355         }
6356       }
6357     } else {
6358       if (perm) {
6359         for (b = 0; b < fdof; b++) {
6360           if ((cind < fcdof) && (b == fcdofs[cind])) {
6361             ++cind;
6362             continue;
6363           }
6364           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6365         }
6366       } else {
6367         for (b = 0; b < fdof; b++) {
6368           if ((cind < fcdof) && (b == fcdofs[cind])) {
6369             ++cind;
6370             continue;
6371           }
6372           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6373         }
6374       }
6375     }
6376   }
6377   *offset += fdof;
6378   PetscFunctionReturn(PETSC_SUCCESS);
6379 }
6380 
6381 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[])
6382 {
6383   PetscScalar    *a;
6384   PetscInt        fdof, foff, fcdof, foffset = *offset;
6385   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6386   PetscInt        Nc, cind = 0, ncind = 0, b;
6387   PetscBool       ncSet, fcSet;
6388 
6389   PetscFunctionBegin;
6390   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6391   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6392   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6393   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6394   a = &array[foff];
6395   if (fcdof) {
6396     /* We just override fcdof and fcdofs with Ncc and comps */
6397     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6398     if (clperm) {
6399       if (perm) {
6400         if (comps) {
6401           for (b = 0; b < fdof; b++) {
6402             ncSet = fcSet = PETSC_FALSE;
6403             if (b % Nc == comps[ncind]) {
6404               ncind = (ncind + 1) % Ncc;
6405               ncSet = PETSC_TRUE;
6406             }
6407             if ((cind < fcdof) && (b == fcdofs[cind])) {
6408               ++cind;
6409               fcSet = PETSC_TRUE;
6410             }
6411             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6412           }
6413         } else {
6414           for (b = 0; b < fdof; b++) {
6415             if ((cind < fcdof) && (b == fcdofs[cind])) {
6416               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6417               ++cind;
6418             }
6419           }
6420         }
6421       } else {
6422         if (comps) {
6423           for (b = 0; b < fdof; b++) {
6424             ncSet = fcSet = PETSC_FALSE;
6425             if (b % Nc == comps[ncind]) {
6426               ncind = (ncind + 1) % Ncc;
6427               ncSet = PETSC_TRUE;
6428             }
6429             if ((cind < fcdof) && (b == fcdofs[cind])) {
6430               ++cind;
6431               fcSet = PETSC_TRUE;
6432             }
6433             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6434           }
6435         } else {
6436           for (b = 0; b < fdof; b++) {
6437             if ((cind < fcdof) && (b == fcdofs[cind])) {
6438               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6439               ++cind;
6440             }
6441           }
6442         }
6443       }
6444     } else {
6445       if (perm) {
6446         if (comps) {
6447           for (b = 0; b < fdof; b++) {
6448             ncSet = fcSet = PETSC_FALSE;
6449             if (b % Nc == comps[ncind]) {
6450               ncind = (ncind + 1) % Ncc;
6451               ncSet = PETSC_TRUE;
6452             }
6453             if ((cind < fcdof) && (b == fcdofs[cind])) {
6454               ++cind;
6455               fcSet = PETSC_TRUE;
6456             }
6457             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6458           }
6459         } else {
6460           for (b = 0; b < fdof; b++) {
6461             if ((cind < fcdof) && (b == fcdofs[cind])) {
6462               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6463               ++cind;
6464             }
6465           }
6466         }
6467       } else {
6468         if (comps) {
6469           for (b = 0; b < fdof; b++) {
6470             ncSet = fcSet = PETSC_FALSE;
6471             if (b % Nc == comps[ncind]) {
6472               ncind = (ncind + 1) % Ncc;
6473               ncSet = PETSC_TRUE;
6474             }
6475             if ((cind < fcdof) && (b == fcdofs[cind])) {
6476               ++cind;
6477               fcSet = PETSC_TRUE;
6478             }
6479             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6480           }
6481         } else {
6482           for (b = 0; b < fdof; b++) {
6483             if ((cind < fcdof) && (b == fcdofs[cind])) {
6484               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6485               ++cind;
6486             }
6487           }
6488         }
6489       }
6490     }
6491   }
6492   *offset += fdof;
6493   PetscFunctionReturn(PETSC_SUCCESS);
6494 }
6495 
6496 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6497 {
6498   PetscScalar    *array;
6499   const PetscInt *cone, *coneO;
6500   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6501 
6502   PetscFunctionBeginHot;
6503   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6504   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6505   PetscCall(DMPlexGetCone(dm, point, &cone));
6506   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6507   PetscCall(VecGetArray(v, &array));
6508   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6509     const PetscInt cp = !p ? point : cone[p - 1];
6510     const PetscInt o  = !p ? 0 : coneO[p - 1];
6511 
6512     if ((cp < pStart) || (cp >= pEnd)) {
6513       dof = 0;
6514       continue;
6515     }
6516     PetscCall(PetscSectionGetDof(section, cp, &dof));
6517     /* ADD_VALUES */
6518     {
6519       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6520       PetscScalar    *a;
6521       PetscInt        cdof, coff, cind = 0, k;
6522 
6523       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6524       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6525       a = &array[coff];
6526       if (!cdof) {
6527         if (o >= 0) {
6528           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6529         } else {
6530           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6531         }
6532       } else {
6533         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6534         if (o >= 0) {
6535           for (k = 0; k < dof; ++k) {
6536             if ((cind < cdof) && (k == cdofs[cind])) {
6537               ++cind;
6538               continue;
6539             }
6540             a[k] += values[off + k];
6541           }
6542         } else {
6543           for (k = 0; k < dof; ++k) {
6544             if ((cind < cdof) && (k == cdofs[cind])) {
6545               ++cind;
6546               continue;
6547             }
6548             a[k] += values[off + dof - k - 1];
6549           }
6550         }
6551       }
6552     }
6553   }
6554   PetscCall(VecRestoreArray(v, &array));
6555   PetscFunctionReturn(PETSC_SUCCESS);
6556 }
6557 
6558 /*@C
6559   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6560 
6561   Not collective
6562 
6563   Input Parameters:
6564 + dm - The `DM`
6565 . section - The section describing the layout in `v`, or `NULL` to use the default section
6566 . v - The local vector
6567 . point - The point in the `DM`
6568 . values - The array of values
6569 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6570          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6571 
6572   Level: intermediate
6573 
6574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6575 @*/
6576 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6577 {
6578   PetscSection    clSection;
6579   IS              clPoints;
6580   PetscScalar    *array;
6581   PetscInt       *points = NULL;
6582   const PetscInt *clp, *clperm = NULL;
6583   PetscInt        depth, numFields, numPoints, p, clsize;
6584 
6585   PetscFunctionBeginHot;
6586   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6587   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6588   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6589   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6590   PetscCall(DMPlexGetDepth(dm, &depth));
6591   PetscCall(PetscSectionGetNumFields(section, &numFields));
6592   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6593     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6594     PetscFunctionReturn(PETSC_SUCCESS);
6595   }
6596   /* Get points */
6597   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6598   for (clsize = 0, p = 0; p < numPoints; p++) {
6599     PetscInt dof;
6600     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6601     clsize += dof;
6602   }
6603   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6604   /* Get array */
6605   PetscCall(VecGetArray(v, &array));
6606   /* Get values */
6607   if (numFields > 0) {
6608     PetscInt offset = 0, f;
6609     for (f = 0; f < numFields; ++f) {
6610       const PetscInt    **perms = NULL;
6611       const PetscScalar **flips = NULL;
6612 
6613       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6614       switch (mode) {
6615       case INSERT_VALUES:
6616         for (p = 0; p < numPoints; p++) {
6617           const PetscInt     point = points[2 * p];
6618           const PetscInt    *perm  = perms ? perms[p] : NULL;
6619           const PetscScalar *flip  = flips ? flips[p] : NULL;
6620           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6621         }
6622         break;
6623       case INSERT_ALL_VALUES:
6624         for (p = 0; p < numPoints; p++) {
6625           const PetscInt     point = points[2 * p];
6626           const PetscInt    *perm  = perms ? perms[p] : NULL;
6627           const PetscScalar *flip  = flips ? flips[p] : NULL;
6628           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6629         }
6630         break;
6631       case INSERT_BC_VALUES:
6632         for (p = 0; p < numPoints; p++) {
6633           const PetscInt     point = points[2 * p];
6634           const PetscInt    *perm  = perms ? perms[p] : NULL;
6635           const PetscScalar *flip  = flips ? flips[p] : NULL;
6636           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6637         }
6638         break;
6639       case ADD_VALUES:
6640         for (p = 0; p < numPoints; p++) {
6641           const PetscInt     point = points[2 * p];
6642           const PetscInt    *perm  = perms ? perms[p] : NULL;
6643           const PetscScalar *flip  = flips ? flips[p] : NULL;
6644           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6645         }
6646         break;
6647       case ADD_ALL_VALUES:
6648         for (p = 0; p < numPoints; p++) {
6649           const PetscInt     point = points[2 * p];
6650           const PetscInt    *perm  = perms ? perms[p] : NULL;
6651           const PetscScalar *flip  = flips ? flips[p] : NULL;
6652           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6653         }
6654         break;
6655       case ADD_BC_VALUES:
6656         for (p = 0; p < numPoints; p++) {
6657           const PetscInt     point = points[2 * p];
6658           const PetscInt    *perm  = perms ? perms[p] : NULL;
6659           const PetscScalar *flip  = flips ? flips[p] : NULL;
6660           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6661         }
6662         break;
6663       default:
6664         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6665       }
6666       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6667     }
6668   } else {
6669     PetscInt            dof, off;
6670     const PetscInt    **perms = NULL;
6671     const PetscScalar **flips = NULL;
6672 
6673     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6674     switch (mode) {
6675     case INSERT_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(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6682       }
6683       break;
6684     case INSERT_ALL_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, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6691       }
6692       break;
6693     case INSERT_BC_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(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6700       }
6701       break;
6702     case ADD_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(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6709       }
6710       break;
6711     case ADD_ALL_VALUES:
6712       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6713         const PetscInt     point = points[2 * p];
6714         const PetscInt    *perm  = perms ? perms[p] : NULL;
6715         const PetscScalar *flip  = flips ? flips[p] : NULL;
6716         PetscCall(PetscSectionGetDof(section, point, &dof));
6717         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6718       }
6719       break;
6720     case ADD_BC_VALUES:
6721       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6722         const PetscInt     point = points[2 * p];
6723         const PetscInt    *perm  = perms ? perms[p] : NULL;
6724         const PetscScalar *flip  = flips ? flips[p] : NULL;
6725         PetscCall(PetscSectionGetDof(section, point, &dof));
6726         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6727       }
6728       break;
6729     default:
6730       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6731     }
6732     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6733   }
6734   /* Cleanup points */
6735   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6736   /* Cleanup array */
6737   PetscCall(VecRestoreArray(v, &array));
6738   PetscFunctionReturn(PETSC_SUCCESS);
6739 }
6740 
6741 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6742 {
6743   const PetscInt *supp, *cone;
6744   PetscScalar    *a;
6745   PetscInt        dim, Ns, dof, off, n = 0;
6746 
6747   PetscFunctionBegin;
6748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6749   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6750   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6751   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6752   if (PetscDefined(USE_DEBUG)) {
6753     PetscInt vStart, vEnd;
6754 
6755     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6756     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);
6757   }
6758   PetscValidScalarPointer(values, 5);
6759 
6760   PetscCall(DMGetDimension(dm, &dim));
6761   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6762   PetscCall(DMPlexGetSupport(dm, point, &supp));
6763   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);
6764   PetscCall(VecGetArray(v, &a));
6765   PetscCall(PetscSectionGetDof(section, point, &dof));
6766   PetscCall(PetscSectionGetOffset(section, point, &off));
6767   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6768   for (PetscInt d = 0; d < dim; ++d) {
6769     // Left edge
6770     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6771     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6772     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6773     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6774     // Right edge
6775     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6776     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6777     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6778     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6779   }
6780   PetscCall(VecRestoreArray(v, &a));
6781   PetscFunctionReturn(PETSC_SUCCESS);
6782 }
6783 
6784 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6785 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6786 {
6787   PetscFunctionBegin;
6788   *contains = PETSC_TRUE;
6789   if (label) {
6790     PetscInt fdof;
6791 
6792     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6793     if (!*contains) {
6794       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6795       *offset += fdof;
6796       PetscFunctionReturn(PETSC_SUCCESS);
6797     }
6798   }
6799   PetscFunctionReturn(PETSC_SUCCESS);
6800 }
6801 
6802 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6803 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)
6804 {
6805   PetscSection    clSection;
6806   IS              clPoints;
6807   PetscScalar    *array;
6808   PetscInt       *points = NULL;
6809   const PetscInt *clp;
6810   PetscInt        numFields, numPoints, p;
6811   PetscInt        offset = 0, f;
6812 
6813   PetscFunctionBeginHot;
6814   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6815   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6816   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6817   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6818   PetscCall(PetscSectionGetNumFields(section, &numFields));
6819   /* Get points */
6820   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6821   /* Get array */
6822   PetscCall(VecGetArray(v, &array));
6823   /* Get values */
6824   for (f = 0; f < numFields; ++f) {
6825     const PetscInt    **perms = NULL;
6826     const PetscScalar **flips = NULL;
6827     PetscBool           contains;
6828 
6829     if (!fieldActive[f]) {
6830       for (p = 0; p < numPoints * 2; p += 2) {
6831         PetscInt fdof;
6832         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6833         offset += fdof;
6834       }
6835       continue;
6836     }
6837     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6838     switch (mode) {
6839     case INSERT_VALUES:
6840       for (p = 0; p < numPoints; p++) {
6841         const PetscInt     point = points[2 * p];
6842         const PetscInt    *perm  = perms ? perms[p] : NULL;
6843         const PetscScalar *flip  = flips ? flips[p] : NULL;
6844         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6845         if (!contains) continue;
6846         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6847       }
6848       break;
6849     case INSERT_ALL_VALUES:
6850       for (p = 0; p < numPoints; p++) {
6851         const PetscInt     point = points[2 * p];
6852         const PetscInt    *perm  = perms ? perms[p] : NULL;
6853         const PetscScalar *flip  = flips ? flips[p] : NULL;
6854         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6855         if (!contains) continue;
6856         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6857       }
6858       break;
6859     case INSERT_BC_VALUES:
6860       for (p = 0; p < numPoints; p++) {
6861         const PetscInt     point = points[2 * p];
6862         const PetscInt    *perm  = perms ? perms[p] : NULL;
6863         const PetscScalar *flip  = flips ? flips[p] : NULL;
6864         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6865         if (!contains) continue;
6866         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6867       }
6868       break;
6869     case ADD_VALUES:
6870       for (p = 0; p < numPoints; p++) {
6871         const PetscInt     point = points[2 * p];
6872         const PetscInt    *perm  = perms ? perms[p] : NULL;
6873         const PetscScalar *flip  = flips ? flips[p] : NULL;
6874         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6875         if (!contains) continue;
6876         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6877       }
6878       break;
6879     case ADD_ALL_VALUES:
6880       for (p = 0; p < numPoints; p++) {
6881         const PetscInt     point = points[2 * p];
6882         const PetscInt    *perm  = perms ? perms[p] : NULL;
6883         const PetscScalar *flip  = flips ? flips[p] : NULL;
6884         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6885         if (!contains) continue;
6886         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6887       }
6888       break;
6889     default:
6890       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6891     }
6892     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6893   }
6894   /* Cleanup points */
6895   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6896   /* Cleanup array */
6897   PetscCall(VecRestoreArray(v, &array));
6898   PetscFunctionReturn(PETSC_SUCCESS);
6899 }
6900 
6901 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6902 {
6903   PetscMPIInt rank;
6904   PetscInt    i, j;
6905 
6906   PetscFunctionBegin;
6907   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6908   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6909   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6910   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6911   numCIndices = numCIndices ? numCIndices : numRIndices;
6912   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6913   for (i = 0; i < numRIndices; i++) {
6914     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6915     for (j = 0; j < numCIndices; j++) {
6916 #if defined(PETSC_USE_COMPLEX)
6917       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6918 #else
6919       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6920 #endif
6921     }
6922     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6923   }
6924   PetscFunctionReturn(PETSC_SUCCESS);
6925 }
6926 
6927 /*
6928   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6929 
6930   Input Parameters:
6931 + section - The section for this data layout
6932 . islocal - Is the section (and thus indices being requested) local or global?
6933 . point   - The point contributing dofs with these indices
6934 . off     - The global offset of this point
6935 . loff    - The local offset of each field
6936 . setBC   - The flag determining whether to include indices of boundary values
6937 . perm    - A permutation of the dofs on this point, or NULL
6938 - indperm - A permutation of the entire indices array, or NULL
6939 
6940   Output Parameter:
6941 . indices - Indices for dofs on this point
6942 
6943   Level: developer
6944 
6945   Note: The indices could be local or global, depending on the value of 'off'.
6946 */
6947 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6948 {
6949   PetscInt        dof;   /* The number of unknowns on this point */
6950   PetscInt        cdof;  /* The number of constraints on this point */
6951   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6952   PetscInt        cind = 0, k;
6953 
6954   PetscFunctionBegin;
6955   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6956   PetscCall(PetscSectionGetDof(section, point, &dof));
6957   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6958   if (!cdof || setBC) {
6959     for (k = 0; k < dof; ++k) {
6960       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6961       const PetscInt ind    = indperm ? indperm[preind] : preind;
6962 
6963       indices[ind] = off + k;
6964     }
6965   } else {
6966     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6967     for (k = 0; k < dof; ++k) {
6968       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6969       const PetscInt ind    = indperm ? indperm[preind] : preind;
6970 
6971       if ((cind < cdof) && (k == cdofs[cind])) {
6972         /* Insert check for returning constrained indices */
6973         indices[ind] = -(off + k + 1);
6974         ++cind;
6975       } else {
6976         indices[ind] = off + k - (islocal ? 0 : cind);
6977       }
6978     }
6979   }
6980   *loff += dof;
6981   PetscFunctionReturn(PETSC_SUCCESS);
6982 }
6983 
6984 /*
6985  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6986 
6987  Input Parameters:
6988 + section - a section (global or local)
6989 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
6990 . point - point within section
6991 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6992 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6993 . setBC - identify constrained (boundary condition) points via involution.
6994 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6995 . permsoff - offset
6996 - indperm - index permutation
6997 
6998  Output Parameter:
6999 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7000 . indices - array to hold indices (as defined by section) of each dof associated with point
7001 
7002  Notes:
7003  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7004  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7005  in the local vector.
7006 
7007  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7008  significant).  It is invalid to call with a global section and setBC=true.
7009 
7010  Developer Note:
7011  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7012  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7013  offset could be obtained from the section instead of passing it explicitly as we do now.
7014 
7015  Example:
7016  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7017  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7018  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7019  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.
7020 
7021  Level: developer
7022 */
7023 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[])
7024 {
7025   PetscInt numFields, foff, f;
7026 
7027   PetscFunctionBegin;
7028   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7029   PetscCall(PetscSectionGetNumFields(section, &numFields));
7030   for (f = 0, foff = 0; f < numFields; ++f) {
7031     PetscInt        fdof, cfdof;
7032     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7033     PetscInt        cind = 0, b;
7034     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7035 
7036     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7037     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7038     if (!cfdof || setBC) {
7039       for (b = 0; b < fdof; ++b) {
7040         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7041         const PetscInt ind    = indperm ? indperm[preind] : preind;
7042 
7043         indices[ind] = off + foff + b;
7044       }
7045     } else {
7046       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7047       for (b = 0; b < fdof; ++b) {
7048         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7049         const PetscInt ind    = indperm ? indperm[preind] : preind;
7050 
7051         if ((cind < cfdof) && (b == fcdofs[cind])) {
7052           indices[ind] = -(off + foff + b + 1);
7053           ++cind;
7054         } else {
7055           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7056         }
7057       }
7058     }
7059     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7060     foffs[f] += fdof;
7061   }
7062   PetscFunctionReturn(PETSC_SUCCESS);
7063 }
7064 
7065 /*
7066   This version believes the globalSection offsets for each field, rather than just the point offset
7067 
7068  . foffs - The offset into 'indices' for each field, since it is segregated by field
7069 
7070  Notes:
7071  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7072  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7073 */
7074 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7075 {
7076   PetscInt numFields, foff, f;
7077 
7078   PetscFunctionBegin;
7079   PetscCall(PetscSectionGetNumFields(section, &numFields));
7080   for (f = 0; f < numFields; ++f) {
7081     PetscInt        fdof, cfdof;
7082     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7083     PetscInt        cind = 0, b;
7084     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7085 
7086     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7087     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7088     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7089     if (!cfdof) {
7090       for (b = 0; b < fdof; ++b) {
7091         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7092         const PetscInt ind    = indperm ? indperm[preind] : preind;
7093 
7094         indices[ind] = foff + b;
7095       }
7096     } else {
7097       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7098       for (b = 0; b < fdof; ++b) {
7099         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7100         const PetscInt ind    = indperm ? indperm[preind] : preind;
7101 
7102         if ((cind < cfdof) && (b == fcdofs[cind])) {
7103           indices[ind] = -(foff + b + 1);
7104           ++cind;
7105         } else {
7106           indices[ind] = foff + b - cind;
7107         }
7108       }
7109     }
7110     foffs[f] += fdof;
7111   }
7112   PetscFunctionReturn(PETSC_SUCCESS);
7113 }
7114 
7115 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)
7116 {
7117   Mat             cMat;
7118   PetscSection    aSec, cSec;
7119   IS              aIS;
7120   PetscInt        aStart = -1, aEnd = -1;
7121   const PetscInt *anchors;
7122   PetscInt        numFields, f, p, q, newP = 0;
7123   PetscInt        newNumPoints = 0, newNumIndices = 0;
7124   PetscInt       *newPoints, *indices, *newIndices;
7125   PetscInt        maxAnchor, maxDof;
7126   PetscInt        newOffsets[32];
7127   PetscInt       *pointMatOffsets[32];
7128   PetscInt       *newPointOffsets[32];
7129   PetscScalar    *pointMat[32];
7130   PetscScalar    *newValues      = NULL, *tmpValues;
7131   PetscBool       anyConstrained = PETSC_FALSE;
7132 
7133   PetscFunctionBegin;
7134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7135   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7136   PetscCall(PetscSectionGetNumFields(section, &numFields));
7137 
7138   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7139   /* if there are point-to-point constraints */
7140   if (aSec) {
7141     PetscCall(PetscArrayzero(newOffsets, 32));
7142     PetscCall(ISGetIndices(aIS, &anchors));
7143     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7144     /* figure out how many points are going to be in the new element matrix
7145      * (we allow double counting, because it's all just going to be summed
7146      * into the global matrix anyway) */
7147     for (p = 0; p < 2 * numPoints; p += 2) {
7148       PetscInt b    = points[p];
7149       PetscInt bDof = 0, bSecDof;
7150 
7151       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7152       if (!bSecDof) continue;
7153       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7154       if (bDof) {
7155         /* this point is constrained */
7156         /* it is going to be replaced by its anchors */
7157         PetscInt bOff, q;
7158 
7159         anyConstrained = PETSC_TRUE;
7160         newNumPoints += bDof;
7161         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7162         for (q = 0; q < bDof; q++) {
7163           PetscInt a = anchors[bOff + q];
7164           PetscInt aDof;
7165 
7166           PetscCall(PetscSectionGetDof(section, a, &aDof));
7167           newNumIndices += aDof;
7168           for (f = 0; f < numFields; ++f) {
7169             PetscInt fDof;
7170 
7171             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7172             newOffsets[f + 1] += fDof;
7173           }
7174         }
7175       } else {
7176         /* this point is not constrained */
7177         newNumPoints++;
7178         newNumIndices += bSecDof;
7179         for (f = 0; f < numFields; ++f) {
7180           PetscInt fDof;
7181 
7182           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7183           newOffsets[f + 1] += fDof;
7184         }
7185       }
7186     }
7187   }
7188   if (!anyConstrained) {
7189     if (outNumPoints) *outNumPoints = 0;
7190     if (outNumIndices) *outNumIndices = 0;
7191     if (outPoints) *outPoints = NULL;
7192     if (outValues) *outValues = NULL;
7193     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7194     PetscFunctionReturn(PETSC_SUCCESS);
7195   }
7196 
7197   if (outNumPoints) *outNumPoints = newNumPoints;
7198   if (outNumIndices) *outNumIndices = newNumIndices;
7199 
7200   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7201 
7202   if (!outPoints && !outValues) {
7203     if (offsets) {
7204       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7205     }
7206     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7207     PetscFunctionReturn(PETSC_SUCCESS);
7208   }
7209 
7210   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7211 
7212   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7213 
7214   /* workspaces */
7215   if (numFields) {
7216     for (f = 0; f < numFields; f++) {
7217       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7218       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7219     }
7220   } else {
7221     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7222     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7223   }
7224 
7225   /* get workspaces for the point-to-point matrices */
7226   if (numFields) {
7227     PetscInt totalOffset, totalMatOffset;
7228 
7229     for (p = 0; p < numPoints; p++) {
7230       PetscInt b    = points[2 * p];
7231       PetscInt bDof = 0, bSecDof;
7232 
7233       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7234       if (!bSecDof) {
7235         for (f = 0; f < numFields; f++) {
7236           newPointOffsets[f][p + 1] = 0;
7237           pointMatOffsets[f][p + 1] = 0;
7238         }
7239         continue;
7240       }
7241       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7242       if (bDof) {
7243         for (f = 0; f < numFields; f++) {
7244           PetscInt fDof, q, bOff, allFDof = 0;
7245 
7246           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7247           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7248           for (q = 0; q < bDof; q++) {
7249             PetscInt a = anchors[bOff + q];
7250             PetscInt aFDof;
7251 
7252             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7253             allFDof += aFDof;
7254           }
7255           newPointOffsets[f][p + 1] = allFDof;
7256           pointMatOffsets[f][p + 1] = fDof * allFDof;
7257         }
7258       } else {
7259         for (f = 0; f < numFields; f++) {
7260           PetscInt fDof;
7261 
7262           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7263           newPointOffsets[f][p + 1] = fDof;
7264           pointMatOffsets[f][p + 1] = 0;
7265         }
7266       }
7267     }
7268     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7269       newPointOffsets[f][0] = totalOffset;
7270       pointMatOffsets[f][0] = totalMatOffset;
7271       for (p = 0; p < numPoints; p++) {
7272         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7273         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7274       }
7275       totalOffset    = newPointOffsets[f][numPoints];
7276       totalMatOffset = pointMatOffsets[f][numPoints];
7277       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7278     }
7279   } else {
7280     for (p = 0; p < numPoints; p++) {
7281       PetscInt b    = points[2 * p];
7282       PetscInt bDof = 0, bSecDof;
7283 
7284       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7285       if (!bSecDof) {
7286         newPointOffsets[0][p + 1] = 0;
7287         pointMatOffsets[0][p + 1] = 0;
7288         continue;
7289       }
7290       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7291       if (bDof) {
7292         PetscInt bOff, q, allDof = 0;
7293 
7294         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7295         for (q = 0; q < bDof; q++) {
7296           PetscInt a = anchors[bOff + q], aDof;
7297 
7298           PetscCall(PetscSectionGetDof(section, a, &aDof));
7299           allDof += aDof;
7300         }
7301         newPointOffsets[0][p + 1] = allDof;
7302         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7303       } else {
7304         newPointOffsets[0][p + 1] = bSecDof;
7305         pointMatOffsets[0][p + 1] = 0;
7306       }
7307     }
7308     newPointOffsets[0][0] = 0;
7309     pointMatOffsets[0][0] = 0;
7310     for (p = 0; p < numPoints; p++) {
7311       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7312       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7313     }
7314     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7315   }
7316 
7317   /* output arrays */
7318   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7319 
7320   /* get the point-to-point matrices; construct newPoints */
7321   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7322   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7323   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7324   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7325   if (numFields) {
7326     for (p = 0, newP = 0; p < numPoints; p++) {
7327       PetscInt b    = points[2 * p];
7328       PetscInt o    = points[2 * p + 1];
7329       PetscInt bDof = 0, bSecDof;
7330 
7331       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7332       if (!bSecDof) continue;
7333       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7334       if (bDof) {
7335         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7336 
7337         fStart[0] = 0;
7338         fEnd[0]   = 0;
7339         for (f = 0; f < numFields; f++) {
7340           PetscInt fDof;
7341 
7342           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7343           fStart[f + 1] = fStart[f] + fDof;
7344           fEnd[f + 1]   = fStart[f + 1];
7345         }
7346         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7347         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7348 
7349         fAnchorStart[0] = 0;
7350         fAnchorEnd[0]   = 0;
7351         for (f = 0; f < numFields; f++) {
7352           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7353 
7354           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7355           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7356         }
7357         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7358         for (q = 0; q < bDof; q++) {
7359           PetscInt a = anchors[bOff + q], aOff;
7360 
7361           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7362           newPoints[2 * (newP + q)]     = a;
7363           newPoints[2 * (newP + q) + 1] = 0;
7364           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7365           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7366         }
7367         newP += bDof;
7368 
7369         if (outValues) {
7370           /* get the point-to-point submatrix */
7371           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]));
7372         }
7373       } else {
7374         newPoints[2 * newP]     = b;
7375         newPoints[2 * newP + 1] = o;
7376         newP++;
7377       }
7378     }
7379   } else {
7380     for (p = 0; p < numPoints; p++) {
7381       PetscInt b    = points[2 * p];
7382       PetscInt o    = points[2 * p + 1];
7383       PetscInt bDof = 0, bSecDof;
7384 
7385       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7386       if (!bSecDof) continue;
7387       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7388       if (bDof) {
7389         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7390 
7391         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7392         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7393 
7394         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7395         for (q = 0; q < bDof; q++) {
7396           PetscInt a = anchors[bOff + q], aOff;
7397 
7398           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7399 
7400           newPoints[2 * (newP + q)]     = a;
7401           newPoints[2 * (newP + q) + 1] = 0;
7402           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7403           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7404         }
7405         newP += bDof;
7406 
7407         /* get the point-to-point submatrix */
7408         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7409       } else {
7410         newPoints[2 * newP]     = b;
7411         newPoints[2 * newP + 1] = o;
7412         newP++;
7413       }
7414     }
7415   }
7416 
7417   if (outValues) {
7418     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7419     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7420     /* multiply constraints on the right */
7421     if (numFields) {
7422       for (f = 0; f < numFields; f++) {
7423         PetscInt oldOff = offsets[f];
7424 
7425         for (p = 0; p < numPoints; p++) {
7426           PetscInt cStart = newPointOffsets[f][p];
7427           PetscInt b      = points[2 * p];
7428           PetscInt c, r, k;
7429           PetscInt dof;
7430 
7431           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7432           if (!dof) continue;
7433           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7434             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7435             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7436 
7437             for (r = 0; r < numIndices; r++) {
7438               for (c = 0; c < nCols; c++) {
7439                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7440               }
7441             }
7442           } else {
7443             /* copy this column as is */
7444             for (r = 0; r < numIndices; r++) {
7445               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7446             }
7447           }
7448           oldOff += dof;
7449         }
7450       }
7451     } else {
7452       PetscInt oldOff = 0;
7453       for (p = 0; p < numPoints; p++) {
7454         PetscInt cStart = newPointOffsets[0][p];
7455         PetscInt b      = points[2 * p];
7456         PetscInt c, r, k;
7457         PetscInt dof;
7458 
7459         PetscCall(PetscSectionGetDof(section, b, &dof));
7460         if (!dof) continue;
7461         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7462           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7463           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7464 
7465           for (r = 0; r < numIndices; r++) {
7466             for (c = 0; c < nCols; c++) {
7467               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7468             }
7469           }
7470         } else {
7471           /* copy this column as is */
7472           for (r = 0; r < numIndices; r++) {
7473             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7474           }
7475         }
7476         oldOff += dof;
7477       }
7478     }
7479 
7480     if (multiplyLeft) {
7481       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7482       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7483       /* multiply constraints transpose on the left */
7484       if (numFields) {
7485         for (f = 0; f < numFields; f++) {
7486           PetscInt oldOff = offsets[f];
7487 
7488           for (p = 0; p < numPoints; p++) {
7489             PetscInt rStart = newPointOffsets[f][p];
7490             PetscInt b      = points[2 * p];
7491             PetscInt c, r, k;
7492             PetscInt dof;
7493 
7494             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7495             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7496               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7497               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7498 
7499               for (r = 0; r < nRows; r++) {
7500                 for (c = 0; c < newNumIndices; c++) {
7501                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7502                 }
7503               }
7504             } else {
7505               /* copy this row as is */
7506               for (r = 0; r < dof; r++) {
7507                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7508               }
7509             }
7510             oldOff += dof;
7511           }
7512         }
7513       } else {
7514         PetscInt oldOff = 0;
7515 
7516         for (p = 0; p < numPoints; p++) {
7517           PetscInt rStart = newPointOffsets[0][p];
7518           PetscInt b      = points[2 * p];
7519           PetscInt c, r, k;
7520           PetscInt dof;
7521 
7522           PetscCall(PetscSectionGetDof(section, b, &dof));
7523           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7524             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7525             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7526 
7527             for (r = 0; r < nRows; r++) {
7528               for (c = 0; c < newNumIndices; c++) {
7529                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7530               }
7531             }
7532           } else {
7533             /* copy this row as is */
7534             for (r = 0; r < dof; r++) {
7535               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7536             }
7537           }
7538           oldOff += dof;
7539         }
7540       }
7541 
7542       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7543     } else {
7544       newValues = tmpValues;
7545     }
7546   }
7547 
7548   /* clean up */
7549   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7550   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7551 
7552   if (numFields) {
7553     for (f = 0; f < numFields; f++) {
7554       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7555       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7556       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7557     }
7558   } else {
7559     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7560     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7561     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7562   }
7563   PetscCall(ISRestoreIndices(aIS, &anchors));
7564 
7565   /* output */
7566   if (outPoints) {
7567     *outPoints = newPoints;
7568   } else {
7569     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7570   }
7571   if (outValues) *outValues = newValues;
7572   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7573   PetscFunctionReturn(PETSC_SUCCESS);
7574 }
7575 
7576 /*@C
7577   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7578 
7579   Not collective
7580 
7581   Input Parameters:
7582 + dm         - The `DM`
7583 . section    - The `PetscSection` describing the points (a local section)
7584 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7585 . point      - The point defining the closure
7586 - useClPerm  - Use the closure point permutation if available
7587 
7588   Output Parameters:
7589 + numIndices - The number of dof indices in the closure of point with the input sections
7590 . indices    - The dof indices
7591 . outOffsets - Array to write the field offsets into, or `NULL`
7592 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7593 
7594   Level: advanced
7595 
7596   Notes:
7597   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7598 
7599   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7600   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7601   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7602   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7603   indices (with the above semantics) are implied.
7604 
7605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7606           `PetscSection`, `DMGetGlobalSection()`
7607 @*/
7608 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7609 {
7610   /* Closure ordering */
7611   PetscSection    clSection;
7612   IS              clPoints;
7613   const PetscInt *clp;
7614   PetscInt       *points;
7615   const PetscInt *clperm = NULL;
7616   /* Dof permutation and sign flips */
7617   const PetscInt    **perms[32] = {NULL};
7618   const PetscScalar **flips[32] = {NULL};
7619   PetscScalar        *valCopy   = NULL;
7620   /* Hanging node constraints */
7621   PetscInt    *pointsC = NULL;
7622   PetscScalar *valuesC = NULL;
7623   PetscInt     NclC, NiC;
7624 
7625   PetscInt *idx;
7626   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7627   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7628 
7629   PetscFunctionBeginHot;
7630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7631   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7632   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7633   if (numIndices) PetscValidIntPointer(numIndices, 6);
7634   if (indices) PetscValidPointer(indices, 7);
7635   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7636   if (values) PetscValidPointer(values, 9);
7637   PetscCall(PetscSectionGetNumFields(section, &Nf));
7638   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7639   PetscCall(PetscArrayzero(offsets, 32));
7640   /* 1) Get points in closure */
7641   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7642   if (useClPerm) {
7643     PetscInt depth, clsize;
7644     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7645     for (clsize = 0, p = 0; p < Ncl; p++) {
7646       PetscInt dof;
7647       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7648       clsize += dof;
7649     }
7650     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7651   }
7652   /* 2) Get number of indices on these points and field offsets from section */
7653   for (p = 0; p < Ncl * 2; p += 2) {
7654     PetscInt dof, fdof;
7655 
7656     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7657     for (f = 0; f < Nf; ++f) {
7658       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7659       offsets[f + 1] += fdof;
7660     }
7661     Ni += dof;
7662   }
7663   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7664   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7665   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7666   for (f = 0; f < PetscMax(1, Nf); ++f) {
7667     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7668     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7669     /* may need to apply sign changes to the element matrix */
7670     if (values && flips[f]) {
7671       PetscInt foffset = offsets[f];
7672 
7673       for (p = 0; p < Ncl; ++p) {
7674         PetscInt           pnt  = points[2 * p], fdof;
7675         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7676 
7677         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7678         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7679         if (flip) {
7680           PetscInt i, j, k;
7681 
7682           if (!valCopy) {
7683             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7684             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7685             *values = valCopy;
7686           }
7687           for (i = 0; i < fdof; ++i) {
7688             PetscScalar fval = flip[i];
7689 
7690             for (k = 0; k < Ni; ++k) {
7691               valCopy[Ni * (foffset + i) + k] *= fval;
7692               valCopy[Ni * k + (foffset + i)] *= fval;
7693             }
7694           }
7695         }
7696         foffset += fdof;
7697       }
7698     }
7699   }
7700   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7701   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7702   if (NclC) {
7703     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7704     for (f = 0; f < PetscMax(1, Nf); ++f) {
7705       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7706       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7707     }
7708     for (f = 0; f < PetscMax(1, Nf); ++f) {
7709       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7710       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7711     }
7712     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7713     Ncl    = NclC;
7714     Ni     = NiC;
7715     points = pointsC;
7716     if (values) *values = valuesC;
7717   }
7718   /* 5) Calculate indices */
7719   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7720   if (Nf) {
7721     PetscInt  idxOff;
7722     PetscBool useFieldOffsets;
7723 
7724     if (outOffsets) {
7725       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7726     }
7727     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7728     if (useFieldOffsets) {
7729       for (p = 0; p < Ncl; ++p) {
7730         const PetscInt pnt = points[p * 2];
7731 
7732         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7733       }
7734     } else {
7735       for (p = 0; p < Ncl; ++p) {
7736         const PetscInt pnt = points[p * 2];
7737 
7738         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7739         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7740          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7741          * global section. */
7742         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7743       }
7744     }
7745   } else {
7746     PetscInt off = 0, idxOff;
7747 
7748     for (p = 0; p < Ncl; ++p) {
7749       const PetscInt  pnt  = points[p * 2];
7750       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7751 
7752       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7753       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7754        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7755       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7756     }
7757   }
7758   /* 6) Cleanup */
7759   for (f = 0; f < PetscMax(1, Nf); ++f) {
7760     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7761     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7762   }
7763   if (NclC) {
7764     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7765   } else {
7766     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7767   }
7768 
7769   if (numIndices) *numIndices = Ni;
7770   if (indices) *indices = idx;
7771   PetscFunctionReturn(PETSC_SUCCESS);
7772 }
7773 
7774 /*@C
7775   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7776 
7777   Not collective
7778 
7779   Input Parameters:
7780 + dm         - The `DM`
7781 . section    - The `PetscSection` describing the points (a local section)
7782 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7783 . point      - The point defining the closure
7784 - useClPerm  - Use the closure point permutation if available
7785 
7786   Output Parameters:
7787 + numIndices - The number of dof indices in the closure of point with the input sections
7788 . indices    - The dof indices
7789 . outOffsets - Array to write the field offsets into, or `NULL`
7790 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7791 
7792   Level: advanced
7793 
7794   Notes:
7795   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7796 
7797   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7798   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7799   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7800   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7801   indices (with the above semantics) are implied.
7802 
7803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7804 @*/
7805 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7806 {
7807   PetscFunctionBegin;
7808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7809   PetscValidPointer(indices, 7);
7810   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7811   PetscFunctionReturn(PETSC_SUCCESS);
7812 }
7813 
7814 /*@C
7815   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7816 
7817   Not collective
7818 
7819   Input Parameters:
7820 + dm - The `DM`
7821 . section - The section describing the layout in `v`, or `NULL` to use the default section
7822 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7823 . A - The matrix
7824 . point - The point in the `DM`
7825 . values - The array of values
7826 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7827 
7828   Level: intermediate
7829 
7830 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7831 @*/
7832 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7833 {
7834   DM_Plex           *mesh = (DM_Plex *)dm->data;
7835   PetscInt          *indices;
7836   PetscInt           numIndices;
7837   const PetscScalar *valuesOrig = values;
7838   PetscErrorCode     ierr;
7839 
7840   PetscFunctionBegin;
7841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7842   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7843   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7844   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7845   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7846   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7847 
7848   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7849 
7850   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7851   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7852   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7853   if (ierr) {
7854     PetscMPIInt rank;
7855 
7856     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7857     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7858     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7859     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7860     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7861     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7862   }
7863   if (mesh->printFEM > 1) {
7864     PetscInt i;
7865     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7866     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7867     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7868   }
7869 
7870   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7871   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7872   PetscFunctionReturn(PETSC_SUCCESS);
7873 }
7874 
7875 /*@C
7876   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7877 
7878   Not collective
7879 
7880   Input Parameters:
7881 + dmRow - The `DM` for the row fields
7882 . sectionRow - The section describing the layout, or `NULL` to use the default section in `dmRow`
7883 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7884 . dmCol - The `DM` for the column fields
7885 . sectionCol - The section describing the layout, or `NULL` to use the default section in `dmCol`
7886 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7887 . A - The matrix
7888 . point - The point in the `DM`
7889 . values - The array of values
7890 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7891 
7892   Level: intermediate
7893 
7894 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7895 @*/
7896 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7897 {
7898   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7899   PetscInt          *indicesRow, *indicesCol;
7900   PetscInt           numIndicesRow, numIndicesCol;
7901   const PetscScalar *valuesOrig = values;
7902   PetscErrorCode     ierr;
7903 
7904   PetscFunctionBegin;
7905   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7906   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7907   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7908   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7909   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7910   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7911   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7912   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7913   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7914   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7915   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7916 
7917   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7918   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7919 
7920   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7921   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7922   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7923   if (ierr) {
7924     PetscMPIInt rank;
7925 
7926     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7927     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7928     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7929     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7930     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7931     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7932   }
7933 
7934   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7935   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7936   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7937   PetscFunctionReturn(PETSC_SUCCESS);
7938 }
7939 
7940 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7941 {
7942   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7943   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7944   PetscInt       *cpoints = NULL;
7945   PetscInt       *findices, *cindices;
7946   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7947   PetscInt        foffsets[32], coffsets[32];
7948   DMPolytopeType  ct;
7949   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7950   PetscErrorCode  ierr;
7951 
7952   PetscFunctionBegin;
7953   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7954   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7955   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7956   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7957   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7958   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7959   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7960   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7961   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7962   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7963   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7964   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7965   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7966   PetscCall(PetscArrayzero(foffsets, 32));
7967   PetscCall(PetscArrayzero(coffsets, 32));
7968   /* Column indices */
7969   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7970   maxFPoints = numCPoints;
7971   /* Compress out points not in the section */
7972   /*   TODO: Squeeze out points with 0 dof as well */
7973   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7974   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7975     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7976       cpoints[q * 2]     = cpoints[p];
7977       cpoints[q * 2 + 1] = cpoints[p + 1];
7978       ++q;
7979     }
7980   }
7981   numCPoints = q;
7982   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7983     PetscInt fdof;
7984 
7985     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7986     if (!dof) continue;
7987     for (f = 0; f < numFields; ++f) {
7988       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7989       coffsets[f + 1] += fdof;
7990     }
7991     numCIndices += dof;
7992   }
7993   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7994   /* Row indices */
7995   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7996   {
7997     DMPlexTransform tr;
7998     DMPolytopeType *rct;
7999     PetscInt       *rsize, *rcone, *rornt, Nt;
8000 
8001     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8002     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8003     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8004     numSubcells = rsize[Nt - 1];
8005     PetscCall(DMPlexTransformDestroy(&tr));
8006   }
8007   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8008   for (r = 0, q = 0; r < numSubcells; ++r) {
8009     /* TODO Map from coarse to fine cells */
8010     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8011     /* Compress out points not in the section */
8012     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8013     for (p = 0; p < numFPoints * 2; p += 2) {
8014       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8015         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8016         if (!dof) continue;
8017         for (s = 0; s < q; ++s)
8018           if (fpoints[p] == ftotpoints[s * 2]) break;
8019         if (s < q) continue;
8020         ftotpoints[q * 2]     = fpoints[p];
8021         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8022         ++q;
8023       }
8024     }
8025     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8026   }
8027   numFPoints = q;
8028   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8029     PetscInt fdof;
8030 
8031     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8032     if (!dof) continue;
8033     for (f = 0; f < numFields; ++f) {
8034       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8035       foffsets[f + 1] += fdof;
8036     }
8037     numFIndices += dof;
8038   }
8039   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8040 
8041   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8042   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8043   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8044   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8045   if (numFields) {
8046     const PetscInt **permsF[32] = {NULL};
8047     const PetscInt **permsC[32] = {NULL};
8048 
8049     for (f = 0; f < numFields; f++) {
8050       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8051       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8052     }
8053     for (p = 0; p < numFPoints; p++) {
8054       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8055       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8056     }
8057     for (p = 0; p < numCPoints; p++) {
8058       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8059       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8060     }
8061     for (f = 0; f < numFields; f++) {
8062       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8063       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8064     }
8065   } else {
8066     const PetscInt **permsF = NULL;
8067     const PetscInt **permsC = NULL;
8068 
8069     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8070     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8071     for (p = 0, off = 0; p < numFPoints; p++) {
8072       const PetscInt *perm = permsF ? permsF[p] : NULL;
8073 
8074       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8075       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8076     }
8077     for (p = 0, off = 0; p < numCPoints; p++) {
8078       const PetscInt *perm = permsC ? permsC[p] : NULL;
8079 
8080       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8081       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8082     }
8083     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8084     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8085   }
8086   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8087   /* TODO: flips */
8088   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8089   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8090   if (ierr) {
8091     PetscMPIInt rank;
8092 
8093     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8094     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8095     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8096     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8097     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8098   }
8099   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8100   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8101   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8102   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8103   PetscFunctionReturn(PETSC_SUCCESS);
8104 }
8105 
8106 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8107 {
8108   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8109   PetscInt       *cpoints = NULL;
8110   PetscInt        foffsets[32], coffsets[32];
8111   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8112   DMPolytopeType  ct;
8113   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8114 
8115   PetscFunctionBegin;
8116   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8117   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8118   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8119   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8120   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8121   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8122   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8123   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8124   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8125   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8126   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8127   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8128   PetscCall(PetscArrayzero(foffsets, 32));
8129   PetscCall(PetscArrayzero(coffsets, 32));
8130   /* Column indices */
8131   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8132   maxFPoints = numCPoints;
8133   /* Compress out points not in the section */
8134   /*   TODO: Squeeze out points with 0 dof as well */
8135   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8136   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8137     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8138       cpoints[q * 2]     = cpoints[p];
8139       cpoints[q * 2 + 1] = cpoints[p + 1];
8140       ++q;
8141     }
8142   }
8143   numCPoints = q;
8144   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8145     PetscInt fdof;
8146 
8147     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8148     if (!dof) continue;
8149     for (f = 0; f < numFields; ++f) {
8150       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8151       coffsets[f + 1] += fdof;
8152     }
8153     numCIndices += dof;
8154   }
8155   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8156   /* Row indices */
8157   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8158   {
8159     DMPlexTransform tr;
8160     DMPolytopeType *rct;
8161     PetscInt       *rsize, *rcone, *rornt, Nt;
8162 
8163     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8164     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8165     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8166     numSubcells = rsize[Nt - 1];
8167     PetscCall(DMPlexTransformDestroy(&tr));
8168   }
8169   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8170   for (r = 0, q = 0; r < numSubcells; ++r) {
8171     /* TODO Map from coarse to fine cells */
8172     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8173     /* Compress out points not in the section */
8174     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8175     for (p = 0; p < numFPoints * 2; p += 2) {
8176       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8177         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8178         if (!dof) continue;
8179         for (s = 0; s < q; ++s)
8180           if (fpoints[p] == ftotpoints[s * 2]) break;
8181         if (s < q) continue;
8182         ftotpoints[q * 2]     = fpoints[p];
8183         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8184         ++q;
8185       }
8186     }
8187     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8188   }
8189   numFPoints = q;
8190   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8191     PetscInt fdof;
8192 
8193     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8194     if (!dof) continue;
8195     for (f = 0; f < numFields; ++f) {
8196       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8197       foffsets[f + 1] += fdof;
8198     }
8199     numFIndices += dof;
8200   }
8201   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8202 
8203   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8204   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8205   if (numFields) {
8206     const PetscInt **permsF[32] = {NULL};
8207     const PetscInt **permsC[32] = {NULL};
8208 
8209     for (f = 0; f < numFields; f++) {
8210       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8211       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8212     }
8213     for (p = 0; p < numFPoints; p++) {
8214       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8215       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8216     }
8217     for (p = 0; p < numCPoints; p++) {
8218       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8219       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8220     }
8221     for (f = 0; f < numFields; f++) {
8222       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8223       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8224     }
8225   } else {
8226     const PetscInt **permsF = NULL;
8227     const PetscInt **permsC = NULL;
8228 
8229     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8230     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8231     for (p = 0, off = 0; p < numFPoints; p++) {
8232       const PetscInt *perm = permsF ? permsF[p] : NULL;
8233 
8234       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8235       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8236     }
8237     for (p = 0, off = 0; p < numCPoints; p++) {
8238       const PetscInt *perm = permsC ? permsC[p] : NULL;
8239 
8240       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8241       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8242     }
8243     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8244     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8245   }
8246   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8247   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8248   PetscFunctionReturn(PETSC_SUCCESS);
8249 }
8250 
8251 /*@C
8252   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8253 
8254   Input Parameter:
8255 . dm   - The `DMPLEX` object
8256 
8257   Output Parameter:
8258 . cellHeight - The height of a cell
8259 
8260   Level: developer
8261 
8262 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8263 @*/
8264 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8265 {
8266   DM_Plex *mesh = (DM_Plex *)dm->data;
8267 
8268   PetscFunctionBegin;
8269   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8270   PetscValidIntPointer(cellHeight, 2);
8271   *cellHeight = mesh->vtkCellHeight;
8272   PetscFunctionReturn(PETSC_SUCCESS);
8273 }
8274 
8275 /*@C
8276   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8277 
8278   Input Parameters:
8279 + dm   - The `DMPLEX` object
8280 - cellHeight - The height of a cell
8281 
8282   Level: developer
8283 
8284 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8285 @*/
8286 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8287 {
8288   DM_Plex *mesh = (DM_Plex *)dm->data;
8289 
8290   PetscFunctionBegin;
8291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8292   mesh->vtkCellHeight = cellHeight;
8293   PetscFunctionReturn(PETSC_SUCCESS);
8294 }
8295 
8296 /*@
8297   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8298 
8299   Input Parameter:
8300 . dm - The `DMPLEX` object
8301 
8302   Output Parameters:
8303 + gcStart - The first ghost cell, or `NULL`
8304 - gcEnd   - The upper bound on ghost cells, or `NULL`
8305 
8306   Level: advanced
8307 
8308 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8309 @*/
8310 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8311 {
8312   DMLabel ctLabel;
8313 
8314   PetscFunctionBegin;
8315   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8316   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8317   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8318   // Reset label for fast lookup
8319   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8320   PetscFunctionReturn(PETSC_SUCCESS);
8321 }
8322 
8323 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8324 {
8325   PetscSection section, globalSection;
8326   PetscInt    *numbers, p;
8327 
8328   PetscFunctionBegin;
8329   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8330   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8331   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8332   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8333   PetscCall(PetscSectionSetUp(section));
8334   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8335   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8336   for (p = pStart; p < pEnd; ++p) {
8337     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8338     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8339     else numbers[p - pStart] += shift;
8340   }
8341   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8342   if (globalSize) {
8343     PetscLayout layout;
8344     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8345     PetscCall(PetscLayoutGetSize(layout, globalSize));
8346     PetscCall(PetscLayoutDestroy(&layout));
8347   }
8348   PetscCall(PetscSectionDestroy(&section));
8349   PetscCall(PetscSectionDestroy(&globalSection));
8350   PetscFunctionReturn(PETSC_SUCCESS);
8351 }
8352 
8353 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8354 {
8355   PetscInt cellHeight, cStart, cEnd;
8356 
8357   PetscFunctionBegin;
8358   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8359   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8360   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8361   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8362   PetscFunctionReturn(PETSC_SUCCESS);
8363 }
8364 
8365 /*@
8366   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8367 
8368   Input Parameter:
8369 . dm   - The `DMPLEX` object
8370 
8371   Output Parameter:
8372 . globalCellNumbers - Global cell numbers for all cells on this process
8373 
8374   Level: developer
8375 
8376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8377 @*/
8378 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8379 {
8380   DM_Plex *mesh = (DM_Plex *)dm->data;
8381 
8382   PetscFunctionBegin;
8383   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8384   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8385   *globalCellNumbers = mesh->globalCellNumbers;
8386   PetscFunctionReturn(PETSC_SUCCESS);
8387 }
8388 
8389 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8390 {
8391   PetscInt vStart, vEnd;
8392 
8393   PetscFunctionBegin;
8394   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8395   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8396   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8397   PetscFunctionReturn(PETSC_SUCCESS);
8398 }
8399 
8400 /*@
8401   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8402 
8403   Input Parameter:
8404 . dm   - The `DMPLEX` object
8405 
8406   Output Parameter:
8407 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8408 
8409   Level: developer
8410 
8411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8412 @*/
8413 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8414 {
8415   DM_Plex *mesh = (DM_Plex *)dm->data;
8416 
8417   PetscFunctionBegin;
8418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8419   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8420   *globalVertexNumbers = mesh->globalVertexNumbers;
8421   PetscFunctionReturn(PETSC_SUCCESS);
8422 }
8423 
8424 /*@
8425   DMPlexCreatePointNumbering - Create a global numbering for all points.
8426 
8427   Collective
8428 
8429   Input Parameter:
8430 . dm   - The `DMPLEX` object
8431 
8432   Output Parameter:
8433 . globalPointNumbers - Global numbers for all points on this process
8434 
8435   Level: developer
8436 
8437   Notes:
8438   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8439   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8440   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8441   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8442 
8443   The partitioned mesh is
8444 ```
8445  (2)--0--(3)--1--(4)    (1)--0--(2)
8446 ```
8447   and its global numbering is
8448 ```
8449   (3)--0--(4)--1--(5)--2--(6)
8450 ```
8451   Then the global numbering is provided as
8452 ```
8453 [0] Number of indices in set 5
8454 [0] 0 0
8455 [0] 1 1
8456 [0] 2 3
8457 [0] 3 4
8458 [0] 4 -6
8459 [1] Number of indices in set 3
8460 [1] 0 2
8461 [1] 1 5
8462 [1] 2 6
8463 ```
8464 
8465 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8466 @*/
8467 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8468 {
8469   IS        nums[4];
8470   PetscInt  depths[4], gdepths[4], starts[4];
8471   PetscInt  depth, d, shift = 0;
8472   PetscBool empty = PETSC_FALSE;
8473 
8474   PetscFunctionBegin;
8475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8476   PetscCall(DMPlexGetDepth(dm, &depth));
8477   // For unstratified meshes use dim instead of depth
8478   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8479   // If any stratum is empty, we must mark all empty
8480   for (d = 0; d <= depth; ++d) {
8481     PetscInt end;
8482 
8483     depths[d] = depth - d;
8484     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8485     if (!(starts[d] - end)) empty = PETSC_TRUE;
8486   }
8487   if (empty)
8488     for (d = 0; d <= depth; ++d) {
8489       depths[d] = -1;
8490       starts[d] = -1;
8491     }
8492   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8493   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8494   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]);
8495   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8496   for (d = 0; d <= depth; ++d) {
8497     PetscInt pStart, pEnd, gsize;
8498 
8499     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8500     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8501     shift += gsize;
8502   }
8503   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8504   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8505   PetscFunctionReturn(PETSC_SUCCESS);
8506 }
8507 
8508 /*@
8509   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8510 
8511   Input Parameter:
8512 . dm - The `DMPLEX` object
8513 
8514   Output Parameter:
8515 . ranks - The rank field
8516 
8517   Options Database Key:
8518 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8519 
8520   Level: intermediate
8521 
8522 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8523 @*/
8524 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8525 {
8526   DM             rdm;
8527   PetscFE        fe;
8528   PetscScalar   *r;
8529   PetscMPIInt    rank;
8530   DMPolytopeType ct;
8531   PetscInt       dim, cStart, cEnd, c;
8532   PetscBool      simplex;
8533 
8534   PetscFunctionBeginUser;
8535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8536   PetscValidPointer(ranks, 2);
8537   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8538   PetscCall(DMClone(dm, &rdm));
8539   PetscCall(DMGetDimension(rdm, &dim));
8540   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8541   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8542   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8543   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8544   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8545   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8546   PetscCall(PetscFEDestroy(&fe));
8547   PetscCall(DMCreateDS(rdm));
8548   PetscCall(DMCreateGlobalVector(rdm, ranks));
8549   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8550   PetscCall(VecGetArray(*ranks, &r));
8551   for (c = cStart; c < cEnd; ++c) {
8552     PetscScalar *lr;
8553 
8554     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8555     if (lr) *lr = rank;
8556   }
8557   PetscCall(VecRestoreArray(*ranks, &r));
8558   PetscCall(DMDestroy(&rdm));
8559   PetscFunctionReturn(PETSC_SUCCESS);
8560 }
8561 
8562 /*@
8563   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8564 
8565   Input Parameters:
8566 + dm    - The `DMPLEX`
8567 - label - The `DMLabel`
8568 
8569   Output Parameter:
8570 . val - The label value field
8571 
8572   Options Database Key:
8573 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8574 
8575   Level: intermediate
8576 
8577 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8578 @*/
8579 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8580 {
8581   DM           rdm;
8582   PetscFE      fe;
8583   PetscScalar *v;
8584   PetscInt     dim, cStart, cEnd, c;
8585 
8586   PetscFunctionBeginUser;
8587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8588   PetscValidPointer(label, 2);
8589   PetscValidPointer(val, 3);
8590   PetscCall(DMClone(dm, &rdm));
8591   PetscCall(DMGetDimension(rdm, &dim));
8592   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8593   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8594   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8595   PetscCall(PetscFEDestroy(&fe));
8596   PetscCall(DMCreateDS(rdm));
8597   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8598   PetscCall(DMCreateGlobalVector(rdm, val));
8599   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8600   PetscCall(VecGetArray(*val, &v));
8601   for (c = cStart; c < cEnd; ++c) {
8602     PetscScalar *lv;
8603     PetscInt     cval;
8604 
8605     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8606     PetscCall(DMLabelGetValue(label, c, &cval));
8607     *lv = cval;
8608   }
8609   PetscCall(VecRestoreArray(*val, &v));
8610   PetscCall(DMDestroy(&rdm));
8611   PetscFunctionReturn(PETSC_SUCCESS);
8612 }
8613 
8614 /*@
8615   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8616 
8617   Input Parameter:
8618 . dm - The `DMPLEX` object
8619 
8620   Level: developer
8621 
8622   Notes:
8623   This is a useful diagnostic when creating meshes programmatically.
8624 
8625   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8626 
8627 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8628 @*/
8629 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8630 {
8631   PetscSection    coneSection, supportSection;
8632   const PetscInt *cone, *support;
8633   PetscInt        coneSize, c, supportSize, s;
8634   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8635   PetscBool       storagecheck = PETSC_TRUE;
8636 
8637   PetscFunctionBegin;
8638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8639   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8640   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8641   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8642   /* Check that point p is found in the support of its cone points, and vice versa */
8643   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8644   for (p = pStart; p < pEnd; ++p) {
8645     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8646     PetscCall(DMPlexGetCone(dm, p, &cone));
8647     for (c = 0; c < coneSize; ++c) {
8648       PetscBool dup = PETSC_FALSE;
8649       PetscInt  d;
8650       for (d = c - 1; d >= 0; --d) {
8651         if (cone[c] == cone[d]) {
8652           dup = PETSC_TRUE;
8653           break;
8654         }
8655       }
8656       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8657       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8658       for (s = 0; s < supportSize; ++s) {
8659         if (support[s] == p) break;
8660       }
8661       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8662         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8663         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8664         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8665         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8666         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8667         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8668         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]);
8669         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8670       }
8671     }
8672     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8673     if (p != pp) {
8674       storagecheck = PETSC_FALSE;
8675       continue;
8676     }
8677     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8678     PetscCall(DMPlexGetSupport(dm, p, &support));
8679     for (s = 0; s < supportSize; ++s) {
8680       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8681       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8682       for (c = 0; c < coneSize; ++c) {
8683         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8684         if (cone[c] != pp) {
8685           c = 0;
8686           break;
8687         }
8688         if (cone[c] == p) break;
8689       }
8690       if (c >= coneSize) {
8691         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8692         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8693         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8694         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8695         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8696         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8697         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8698       }
8699     }
8700   }
8701   if (storagecheck) {
8702     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8703     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8704     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8705   }
8706   PetscFunctionReturn(PETSC_SUCCESS);
8707 }
8708 
8709 /*
8710   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.
8711 */
8712 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8713 {
8714   DMPolytopeType  cct;
8715   PetscInt        ptpoints[4];
8716   const PetscInt *cone, *ccone, *ptcone;
8717   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8718 
8719   PetscFunctionBegin;
8720   *unsplit = 0;
8721   switch (ct) {
8722   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8723     ptpoints[npt++] = c;
8724     break;
8725   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8726     PetscCall(DMPlexGetCone(dm, c, &cone));
8727     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8728     for (cp = 0; cp < coneSize; ++cp) {
8729       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8730       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8731     }
8732     break;
8733   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8734   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8735     PetscCall(DMPlexGetCone(dm, c, &cone));
8736     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8737     for (cp = 0; cp < coneSize; ++cp) {
8738       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8739       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8740       for (ccp = 0; ccp < cconeSize; ++ccp) {
8741         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8742         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8743           PetscInt p;
8744           for (p = 0; p < npt; ++p)
8745             if (ptpoints[p] == ccone[ccp]) break;
8746           if (p == npt) ptpoints[npt++] = ccone[ccp];
8747         }
8748       }
8749     }
8750     break;
8751   default:
8752     break;
8753   }
8754   for (pt = 0; pt < npt; ++pt) {
8755     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8756     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8757   }
8758   PetscFunctionReturn(PETSC_SUCCESS);
8759 }
8760 
8761 /*@
8762   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8763 
8764   Input Parameters:
8765 + dm - The `DMPLEX` object
8766 - cellHeight - Normally 0
8767 
8768   Level: developer
8769 
8770   Notes:
8771   This is a useful diagnostic when creating meshes programmatically.
8772   Currently applicable only to homogeneous simplex or tensor meshes.
8773 
8774   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8775 
8776 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8777 @*/
8778 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8779 {
8780   DMPlexInterpolatedFlag interp;
8781   DMPolytopeType         ct;
8782   PetscInt               vStart, vEnd, cStart, cEnd, c;
8783 
8784   PetscFunctionBegin;
8785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8786   PetscCall(DMPlexIsInterpolated(dm, &interp));
8787   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8788   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8789   for (c = cStart; c < cEnd; ++c) {
8790     PetscInt *closure = NULL;
8791     PetscInt  coneSize, closureSize, cl, Nv = 0;
8792 
8793     PetscCall(DMPlexGetCellType(dm, c, &ct));
8794     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8795     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8796     if (interp == DMPLEX_INTERPOLATED_FULL) {
8797       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8798       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));
8799     }
8800     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8801     for (cl = 0; cl < closureSize * 2; cl += 2) {
8802       const PetscInt p = closure[cl];
8803       if ((p >= vStart) && (p < vEnd)) ++Nv;
8804     }
8805     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8806     /* Special Case: Tensor faces with identified vertices */
8807     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8808       PetscInt unsplit;
8809 
8810       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8811       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8812     }
8813     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));
8814   }
8815   PetscFunctionReturn(PETSC_SUCCESS);
8816 }
8817 
8818 /*@
8819   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8820 
8821   Collective
8822 
8823   Input Parameters:
8824 + dm - The `DMPLEX` object
8825 - cellHeight - Normally 0
8826 
8827   Level: developer
8828 
8829   Notes:
8830   This is a useful diagnostic when creating meshes programmatically.
8831   This routine is only relevant for meshes that are fully interpolated across all ranks.
8832   It will error out if a partially interpolated mesh is given on some rank.
8833   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8834 
8835   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8836 
8837 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8838 @*/
8839 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8840 {
8841   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8842   DMPlexInterpolatedFlag interpEnum;
8843 
8844   PetscFunctionBegin;
8845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8846   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8847   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8848   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8849     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8850     PetscFunctionReturn(PETSC_SUCCESS);
8851   }
8852 
8853   PetscCall(DMGetDimension(dm, &dim));
8854   PetscCall(DMPlexGetDepth(dm, &depth));
8855   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8856   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8857     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8858     for (c = cStart; c < cEnd; ++c) {
8859       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8860       const DMPolytopeType *faceTypes;
8861       DMPolytopeType        ct;
8862       PetscInt              numFaces, coneSize, f;
8863       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8864 
8865       PetscCall(DMPlexGetCellType(dm, c, &ct));
8866       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8867       if (unsplit) continue;
8868       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8869       PetscCall(DMPlexGetCone(dm, c, &cone));
8870       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8871       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8872       for (cl = 0; cl < closureSize * 2; cl += 2) {
8873         const PetscInt p = closure[cl];
8874         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8875       }
8876       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8877       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);
8878       for (f = 0; f < numFaces; ++f) {
8879         DMPolytopeType fct;
8880         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8881 
8882         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8883         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8884         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8885           const PetscInt p = fclosure[cl];
8886           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8887         }
8888         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]);
8889         for (v = 0; v < fnumCorners; ++v) {
8890           if (fclosure[v] != faces[fOff + v]) {
8891             PetscInt v1;
8892 
8893             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8894             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8895             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8896             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8897             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8898             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]);
8899           }
8900         }
8901         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8902         fOff += faceSizes[f];
8903       }
8904       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8905       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8906     }
8907   }
8908   PetscFunctionReturn(PETSC_SUCCESS);
8909 }
8910 
8911 /*@
8912   DMPlexCheckGeometry - Check the geometry of mesh cells
8913 
8914   Input Parameter:
8915 . dm - The `DMPLEX` object
8916 
8917   Level: developer
8918 
8919   Notes:
8920   This is a useful diagnostic when creating meshes programmatically.
8921 
8922   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8923 
8924 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8925 @*/
8926 PetscErrorCode DMPlexCheckGeometry(DM dm)
8927 {
8928   Vec       coordinates;
8929   PetscReal detJ, J[9], refVol = 1.0;
8930   PetscReal vol;
8931   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8932 
8933   PetscFunctionBegin;
8934   PetscCall(DMGetDimension(dm, &dim));
8935   PetscCall(DMGetCoordinateDim(dm, &dE));
8936   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8937   PetscCall(DMPlexGetDepth(dm, &depth));
8938   for (d = 0; d < dim; ++d) refVol *= 2.0;
8939   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8940   /* Make sure local coordinates are created, because that step is collective */
8941   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8942   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8943   for (c = cStart; c < cEnd; ++c) {
8944     DMPolytopeType ct;
8945     PetscInt       unsplit;
8946     PetscBool      ignoreZeroVol = PETSC_FALSE;
8947 
8948     PetscCall(DMPlexGetCellType(dm, c, &ct));
8949     switch (ct) {
8950     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8951     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8952     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8953       ignoreZeroVol = PETSC_TRUE;
8954       break;
8955     default:
8956       break;
8957     }
8958     switch (ct) {
8959     case DM_POLYTOPE_TRI_PRISM:
8960     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8961     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8962     case DM_POLYTOPE_PYRAMID:
8963       continue;
8964     default:
8965       break;
8966     }
8967     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8968     if (unsplit) continue;
8969     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8970     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);
8971     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8972     /* This should work with periodicity since DG coordinates should be used */
8973     if (depth > 1) {
8974       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8975       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);
8976       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8977     }
8978   }
8979   PetscFunctionReturn(PETSC_SUCCESS);
8980 }
8981 
8982 /*@
8983   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
8984 
8985   Collective
8986 
8987   Input Parameters:
8988 + dm - The `DMPLEX` object
8989 . pointSF - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
8990 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8991 
8992   Level: developer
8993 
8994   Notes:
8995   This is mainly intended for debugging/testing purposes.
8996 
8997   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8998 
8999   Extra roots can come from priodic cuts, where additional points appear on the boundary
9000 
9001 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9002 @*/
9003 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9004 {
9005   PetscInt           l, nleaves, nroots, overlap;
9006   const PetscInt    *locals;
9007   const PetscSFNode *remotes;
9008   PetscBool          distributed;
9009   MPI_Comm           comm;
9010   PetscMPIInt        rank;
9011 
9012   PetscFunctionBegin;
9013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9014   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9015   else pointSF = dm->sf;
9016   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9017   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9018   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9019   {
9020     PetscMPIInt mpiFlag;
9021 
9022     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9023     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9024   }
9025   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9026   PetscCall(DMPlexIsDistributed(dm, &distributed));
9027   if (!distributed) {
9028     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);
9029     PetscFunctionReturn(PETSC_SUCCESS);
9030   }
9031   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);
9032   PetscCall(DMPlexGetOverlap(dm, &overlap));
9033 
9034   /* Check SF graph is compatible with DMPlex chart */
9035   {
9036     PetscInt pStart, pEnd, maxLeaf;
9037 
9038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9039     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9040     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9041     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9042   }
9043 
9044   /* Check Point SF has no local points referenced */
9045   for (l = 0; l < nleaves; l++) {
9046     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);
9047   }
9048 
9049   /* Check there are no cells in interface */
9050   if (!overlap) {
9051     PetscInt cellHeight, cStart, cEnd;
9052 
9053     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9054     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9055     for (l = 0; l < nleaves; ++l) {
9056       const PetscInt point = locals ? locals[l] : l;
9057 
9058       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9059     }
9060   }
9061 
9062   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9063   {
9064     const PetscInt *rootdegree;
9065 
9066     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9067     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9068     for (l = 0; l < nleaves; ++l) {
9069       const PetscInt  point = locals ? locals[l] : l;
9070       const PetscInt *cone;
9071       PetscInt        coneSize, c, idx;
9072 
9073       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9074       PetscCall(DMPlexGetCone(dm, point, &cone));
9075       for (c = 0; c < coneSize; ++c) {
9076         if (!rootdegree[cone[c]]) {
9077           if (locals) {
9078             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9079           } else {
9080             idx = (cone[c] < nleaves) ? cone[c] : -1;
9081           }
9082           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9083         }
9084       }
9085     }
9086   }
9087   PetscFunctionReturn(PETSC_SUCCESS);
9088 }
9089 
9090 /*@
9091   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9092 
9093   Input Parameter:
9094 . dm - The `DMPLEX` object
9095 
9096   Level: developer
9097 
9098   Notes:
9099   This is a useful diagnostic when creating meshes programmatically.
9100 
9101   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9102 
9103   Currently does not include `DMPlexCheckCellShape()`.
9104 
9105 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9106 @*/
9107 PetscErrorCode DMPlexCheck(DM dm)
9108 {
9109   PetscInt cellHeight;
9110 
9111   PetscFunctionBegin;
9112   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9113   PetscCall(DMPlexCheckSymmetry(dm));
9114   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9115   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9116   PetscCall(DMPlexCheckGeometry(dm));
9117   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9118   PetscCall(DMPlexCheckInterfaceCones(dm));
9119   PetscFunctionReturn(PETSC_SUCCESS);
9120 }
9121 
9122 typedef struct cell_stats {
9123   PetscReal min, max, sum, squaresum;
9124   PetscInt  count;
9125 } cell_stats_t;
9126 
9127 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9128 {
9129   PetscInt i, N = *len;
9130 
9131   for (i = 0; i < N; i++) {
9132     cell_stats_t *A = (cell_stats_t *)a;
9133     cell_stats_t *B = (cell_stats_t *)b;
9134 
9135     B->min = PetscMin(A->min, B->min);
9136     B->max = PetscMax(A->max, B->max);
9137     B->sum += A->sum;
9138     B->squaresum += A->squaresum;
9139     B->count += A->count;
9140   }
9141 }
9142 
9143 /*@
9144   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9145 
9146   Collective
9147 
9148   Input Parameters:
9149 + dm        - The `DMPLEX` object
9150 . output    - If true, statistics will be displayed on `stdout`
9151 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9152 
9153   Level: developer
9154 
9155   Notes:
9156   This is mainly intended for debugging/testing purposes.
9157 
9158   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9159 
9160 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9161 @*/
9162 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9163 {
9164   DM           dmCoarse;
9165   cell_stats_t stats, globalStats;
9166   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9167   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9168   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9169   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9170   PetscMPIInt  rank, size;
9171 
9172   PetscFunctionBegin;
9173   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9174   stats.min = PETSC_MAX_REAL;
9175   stats.max = PETSC_MIN_REAL;
9176   stats.sum = stats.squaresum = 0.;
9177   stats.count                 = 0;
9178 
9179   PetscCallMPI(MPI_Comm_size(comm, &size));
9180   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9181   PetscCall(DMGetCoordinateDim(dm, &cdim));
9182   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9183   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9184   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9185   for (c = cStart; c < cEnd; c++) {
9186     PetscInt  i;
9187     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9188 
9189     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9190     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9191     for (i = 0; i < PetscSqr(cdim); ++i) {
9192       frobJ += J[i] * J[i];
9193       frobInvJ += invJ[i] * invJ[i];
9194     }
9195     cond2 = frobJ * frobInvJ;
9196     cond  = PetscSqrtReal(cond2);
9197 
9198     stats.min = PetscMin(stats.min, cond);
9199     stats.max = PetscMax(stats.max, cond);
9200     stats.sum += cond;
9201     stats.squaresum += cond2;
9202     stats.count++;
9203     if (output && cond > limit) {
9204       PetscSection coordSection;
9205       Vec          coordsLocal;
9206       PetscScalar *coords = NULL;
9207       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9208 
9209       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9210       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9211       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9212       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9213       for (i = 0; i < Nv / cdim; ++i) {
9214         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9215         for (d = 0; d < cdim; ++d) {
9216           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9217           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9218         }
9219         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9220       }
9221       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9222       for (cl = 0; cl < clSize * 2; cl += 2) {
9223         const PetscInt edge = closure[cl];
9224 
9225         if ((edge >= eStart) && (edge < eEnd)) {
9226           PetscReal len;
9227 
9228           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9229           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9230         }
9231       }
9232       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9233       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9234     }
9235   }
9236   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9237 
9238   if (size > 1) {
9239     PetscMPIInt  blockLengths[2] = {4, 1};
9240     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9241     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9242     MPI_Op       statReduce;
9243 
9244     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9245     PetscCallMPI(MPI_Type_commit(&statType));
9246     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9247     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9248     PetscCallMPI(MPI_Op_free(&statReduce));
9249     PetscCallMPI(MPI_Type_free(&statType));
9250   } else {
9251     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9252   }
9253   if (rank == 0) {
9254     count = globalStats.count;
9255     min   = globalStats.min;
9256     max   = globalStats.max;
9257     mean  = globalStats.sum / globalStats.count;
9258     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9259   }
9260 
9261   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));
9262   PetscCall(PetscFree2(J, invJ));
9263 
9264   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9265   if (dmCoarse) {
9266     PetscBool isplex;
9267 
9268     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9269     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9270   }
9271   PetscFunctionReturn(PETSC_SUCCESS);
9272 }
9273 
9274 /*@
9275   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9276   orthogonal quality below given tolerance.
9277 
9278   Collective
9279 
9280   Input Parameters:
9281 + dm   - The `DMPLEX` object
9282 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9283 - atol - [0, 1] Absolute tolerance for tagging cells.
9284 
9285   Output Parameters:
9286 + OrthQual      - `Vec` containing orthogonal quality per cell
9287 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9288 
9289   Options Database Keys:
9290 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9291 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9292 
9293   Level: intermediate
9294 
9295   Notes:
9296   Orthogonal quality is given by the following formula:
9297 
9298   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9299 
9300   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
9301   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9302   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9303   calculating the cosine of the angle between these vectors.
9304 
9305   Orthogonal quality ranges from 1 (best) to 0 (worst).
9306 
9307   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9308   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9309 
9310   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9311 
9312 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9313 @*/
9314 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9315 {
9316   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9317   PetscInt              *idx;
9318   PetscScalar           *oqVals;
9319   const PetscScalar     *cellGeomArr, *faceGeomArr;
9320   PetscReal             *ci, *fi, *Ai;
9321   MPI_Comm               comm;
9322   Vec                    cellgeom, facegeom;
9323   DM                     dmFace, dmCell;
9324   IS                     glob;
9325   ISLocalToGlobalMapping ltog;
9326   PetscViewer            vwr;
9327 
9328   PetscFunctionBegin;
9329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9330   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9331   PetscValidPointer(OrthQual, 4);
9332   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9333   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9334   PetscCall(DMGetDimension(dm, &nc));
9335   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9336   {
9337     DMPlexInterpolatedFlag interpFlag;
9338 
9339     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9340     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9341       PetscMPIInt rank;
9342 
9343       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9344       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9345     }
9346   }
9347   if (OrthQualLabel) {
9348     PetscValidPointer(OrthQualLabel, 5);
9349     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9350     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9351   } else {
9352     *OrthQualLabel = NULL;
9353   }
9354   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9355   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9356   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9357   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9358   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9359   PetscCall(VecCreate(comm, OrthQual));
9360   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9361   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9362   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9363   PetscCall(VecSetUp(*OrthQual));
9364   PetscCall(ISDestroy(&glob));
9365   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9366   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9367   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9368   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9369   PetscCall(VecGetDM(cellgeom, &dmCell));
9370   PetscCall(VecGetDM(facegeom, &dmFace));
9371   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9372   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9373     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9374     PetscInt         cellarr[2], *adj = NULL;
9375     PetscScalar     *cArr, *fArr;
9376     PetscReal        minvalc = 1.0, minvalf = 1.0;
9377     PetscFVCellGeom *cg;
9378 
9379     idx[cellIter] = cell - cStart;
9380     cellarr[0]    = cell;
9381     /* Make indexing into cellGeom easier */
9382     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9383     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9384     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9385     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9386     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9387       PetscInt         i;
9388       const PetscInt   neigh  = adj[cellneigh];
9389       PetscReal        normci = 0, normfi = 0, normai = 0;
9390       PetscFVCellGeom *cgneigh;
9391       PetscFVFaceGeom *fg;
9392 
9393       /* Don't count ourselves in the neighbor list */
9394       if (neigh == cell) continue;
9395       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9396       cellarr[1] = neigh;
9397       {
9398         PetscInt        numcovpts;
9399         const PetscInt *covpts;
9400 
9401         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9402         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9403         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9404       }
9405 
9406       /* Compute c_i, f_i and their norms */
9407       for (i = 0; i < nc; i++) {
9408         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9409         fi[i] = fg->centroid[i] - cg->centroid[i];
9410         Ai[i] = fg->normal[i];
9411         normci += PetscPowReal(ci[i], 2);
9412         normfi += PetscPowReal(fi[i], 2);
9413         normai += PetscPowReal(Ai[i], 2);
9414       }
9415       normci = PetscSqrtReal(normci);
9416       normfi = PetscSqrtReal(normfi);
9417       normai = PetscSqrtReal(normai);
9418 
9419       /* Normalize and compute for each face-cell-normal pair */
9420       for (i = 0; i < nc; i++) {
9421         ci[i] = ci[i] / normci;
9422         fi[i] = fi[i] / normfi;
9423         Ai[i] = Ai[i] / normai;
9424         /* PetscAbs because I don't know if normals are guaranteed to point out */
9425         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9426         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9427       }
9428       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9429       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9430     }
9431     PetscCall(PetscFree(adj));
9432     PetscCall(PetscFree2(cArr, fArr));
9433     /* Defer to cell if they're equal */
9434     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9435     if (OrthQualLabel) {
9436       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9437     }
9438   }
9439   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9440   PetscCall(VecAssemblyBegin(*OrthQual));
9441   PetscCall(VecAssemblyEnd(*OrthQual));
9442   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9443   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9444   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9445   if (OrthQualLabel) {
9446     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9447   }
9448   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9449   PetscCall(PetscViewerDestroy(&vwr));
9450   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9451   PetscFunctionReturn(PETSC_SUCCESS);
9452 }
9453 
9454 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9455  * interpolator construction */
9456 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9457 {
9458   PetscSection section, newSection, gsection;
9459   PetscSF      sf;
9460   PetscBool    hasConstraints, ghasConstraints;
9461 
9462   PetscFunctionBegin;
9463   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9464   PetscValidPointer(odm, 2);
9465   PetscCall(DMGetLocalSection(dm, &section));
9466   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9467   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9468   if (!ghasConstraints) {
9469     PetscCall(PetscObjectReference((PetscObject)dm));
9470     *odm = dm;
9471     PetscFunctionReturn(PETSC_SUCCESS);
9472   }
9473   PetscCall(DMClone(dm, odm));
9474   PetscCall(DMCopyFields(dm, *odm));
9475   PetscCall(DMGetLocalSection(*odm, &newSection));
9476   PetscCall(DMGetPointSF(*odm, &sf));
9477   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9478   PetscCall(DMSetGlobalSection(*odm, gsection));
9479   PetscCall(PetscSectionDestroy(&gsection));
9480   PetscFunctionReturn(PETSC_SUCCESS);
9481 }
9482 
9483 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9484 {
9485   DM        dmco, dmfo;
9486   Mat       interpo;
9487   Vec       rscale;
9488   Vec       cglobalo, clocal;
9489   Vec       fglobal, fglobalo, flocal;
9490   PetscBool regular;
9491 
9492   PetscFunctionBegin;
9493   PetscCall(DMGetFullDM(dmc, &dmco));
9494   PetscCall(DMGetFullDM(dmf, &dmfo));
9495   PetscCall(DMSetCoarseDM(dmfo, dmco));
9496   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9497   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9498   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9499   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9500   PetscCall(DMCreateLocalVector(dmc, &clocal));
9501   PetscCall(VecSet(cglobalo, 0.));
9502   PetscCall(VecSet(clocal, 0.));
9503   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9504   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9505   PetscCall(DMCreateLocalVector(dmf, &flocal));
9506   PetscCall(VecSet(fglobal, 0.));
9507   PetscCall(VecSet(fglobalo, 0.));
9508   PetscCall(VecSet(flocal, 0.));
9509   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9510   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9511   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9512   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9513   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9514   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9515   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9516   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9517   *shift = fglobal;
9518   PetscCall(VecDestroy(&flocal));
9519   PetscCall(VecDestroy(&fglobalo));
9520   PetscCall(VecDestroy(&clocal));
9521   PetscCall(VecDestroy(&cglobalo));
9522   PetscCall(VecDestroy(&rscale));
9523   PetscCall(MatDestroy(&interpo));
9524   PetscCall(DMDestroy(&dmfo));
9525   PetscCall(DMDestroy(&dmco));
9526   PetscFunctionReturn(PETSC_SUCCESS);
9527 }
9528 
9529 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9530 {
9531   PetscObject shifto;
9532   Vec         shift;
9533 
9534   PetscFunctionBegin;
9535   if (!interp) {
9536     Vec rscale;
9537 
9538     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9539     PetscCall(VecDestroy(&rscale));
9540   } else {
9541     PetscCall(PetscObjectReference((PetscObject)interp));
9542   }
9543   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9544   if (!shifto) {
9545     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9546     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9547     shifto = (PetscObject)shift;
9548     PetscCall(VecDestroy(&shift));
9549   }
9550   shift = (Vec)shifto;
9551   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9552   PetscCall(VecAXPY(fineSol, 1.0, shift));
9553   PetscCall(MatDestroy(&interp));
9554   PetscFunctionReturn(PETSC_SUCCESS);
9555 }
9556 
9557 /* Pointwise interpolation
9558      Just code FEM for now
9559      u^f = I u^c
9560      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9561      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9562      I_{ij} = psi^f_i phi^c_j
9563 */
9564 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9565 {
9566   PetscSection gsc, gsf;
9567   PetscInt     m, n;
9568   void        *ctx;
9569   DM           cdm;
9570   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9571 
9572   PetscFunctionBegin;
9573   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9574   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9575   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9576   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9577 
9578   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9579   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9580   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9581   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9582   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9583 
9584   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9585   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9586   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9587   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9588   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9589   if (scaling) {
9590     /* Use naive scaling */
9591     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9592   }
9593   PetscFunctionReturn(PETSC_SUCCESS);
9594 }
9595 
9596 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9597 {
9598   VecScatter ctx;
9599 
9600   PetscFunctionBegin;
9601   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9602   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9603   PetscCall(VecScatterDestroy(&ctx));
9604   PetscFunctionReturn(PETSC_SUCCESS);
9605 }
9606 
9607 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[])
9608 {
9609   const PetscInt Nc = uOff[1] - uOff[0];
9610   PetscInt       c;
9611   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9612 }
9613 
9614 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9615 {
9616   DM           dmc;
9617   PetscDS      ds;
9618   Vec          ones, locmass;
9619   IS           cellIS;
9620   PetscFormKey key;
9621   PetscInt     depth;
9622 
9623   PetscFunctionBegin;
9624   PetscCall(DMClone(dm, &dmc));
9625   PetscCall(DMCopyDisc(dm, dmc));
9626   PetscCall(DMGetDS(dmc, &ds));
9627   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9628   PetscCall(DMCreateGlobalVector(dmc, mass));
9629   PetscCall(DMGetLocalVector(dmc, &ones));
9630   PetscCall(DMGetLocalVector(dmc, &locmass));
9631   PetscCall(DMPlexGetDepth(dmc, &depth));
9632   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9633   PetscCall(VecSet(locmass, 0.0));
9634   PetscCall(VecSet(ones, 1.0));
9635   key.label = NULL;
9636   key.value = 0;
9637   key.field = 0;
9638   key.part  = 0;
9639   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9640   PetscCall(ISDestroy(&cellIS));
9641   PetscCall(VecSet(*mass, 0.0));
9642   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9643   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9644   PetscCall(DMRestoreLocalVector(dmc, &ones));
9645   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9646   PetscCall(DMDestroy(&dmc));
9647   PetscFunctionReturn(PETSC_SUCCESS);
9648 }
9649 
9650 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9651 {
9652   PetscSection gsc, gsf;
9653   PetscInt     m, n;
9654   void        *ctx;
9655   DM           cdm;
9656   PetscBool    regular;
9657 
9658   PetscFunctionBegin;
9659   if (dmFine == dmCoarse) {
9660     DM            dmc;
9661     PetscDS       ds;
9662     PetscWeakForm wf;
9663     Vec           u;
9664     IS            cellIS;
9665     PetscFormKey  key;
9666     PetscInt      depth;
9667 
9668     PetscCall(DMClone(dmFine, &dmc));
9669     PetscCall(DMCopyDisc(dmFine, dmc));
9670     PetscCall(DMGetDS(dmc, &ds));
9671     PetscCall(PetscDSGetWeakForm(ds, &wf));
9672     PetscCall(PetscWeakFormClear(wf));
9673     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9674     PetscCall(DMCreateMatrix(dmc, mass));
9675     PetscCall(DMGetLocalVector(dmc, &u));
9676     PetscCall(DMPlexGetDepth(dmc, &depth));
9677     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9678     PetscCall(MatZeroEntries(*mass));
9679     key.label = NULL;
9680     key.value = 0;
9681     key.field = 0;
9682     key.part  = 0;
9683     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9684     PetscCall(ISDestroy(&cellIS));
9685     PetscCall(DMRestoreLocalVector(dmc, &u));
9686     PetscCall(DMDestroy(&dmc));
9687   } else {
9688     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9689     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9690     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9691     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9692 
9693     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9694     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9695     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9696     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9697 
9698     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9699     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9700     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9701     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9702   }
9703   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9704   PetscFunctionReturn(PETSC_SUCCESS);
9705 }
9706 
9707 /*@
9708   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9709 
9710   Input Parameter:
9711 . dm - The `DMPLEX` object
9712 
9713   Output Parameter:
9714 . regular - The flag
9715 
9716   Level: intermediate
9717 
9718 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9719 @*/
9720 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9721 {
9722   PetscFunctionBegin;
9723   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9724   PetscValidBoolPointer(regular, 2);
9725   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9726   PetscFunctionReturn(PETSC_SUCCESS);
9727 }
9728 
9729 /*@
9730   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9731 
9732   Input Parameters:
9733 + dm - The `DMPLEX` object
9734 - regular - The flag
9735 
9736   Level: intermediate
9737 
9738 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9739 @*/
9740 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9741 {
9742   PetscFunctionBegin;
9743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9744   ((DM_Plex *)dm->data)->regularRefinement = regular;
9745   PetscFunctionReturn(PETSC_SUCCESS);
9746 }
9747 
9748 /*@
9749   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9750   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9751 
9752   Not Collective
9753 
9754   Input Parameter:
9755 . dm - The `DMPLEX` object
9756 
9757   Output Parameters:
9758 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9759 - anchorIS - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9760 
9761   Level: intermediate
9762 
9763 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9764 @*/
9765 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9766 {
9767   DM_Plex *plex = (DM_Plex *)dm->data;
9768 
9769   PetscFunctionBegin;
9770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9771   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9772   if (anchorSection) *anchorSection = plex->anchorSection;
9773   if (anchorIS) *anchorIS = plex->anchorIS;
9774   PetscFunctionReturn(PETSC_SUCCESS);
9775 }
9776 
9777 /*@
9778   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9779   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9780   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9781 
9782   Collective
9783 
9784   Input Parameters:
9785 + dm - The `DMPLEX` object
9786 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9787                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9788 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9789 
9790   Level: intermediate
9791 
9792   Notes:
9793   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9794   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9795 
9796   The reference counts of `anchorSection` and `anchorIS` are incremented.
9797 
9798 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9799 @*/
9800 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9801 {
9802   DM_Plex    *plex = (DM_Plex *)dm->data;
9803   PetscMPIInt result;
9804 
9805   PetscFunctionBegin;
9806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9807   if (anchorSection) {
9808     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9809     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9810     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9811   }
9812   if (anchorIS) {
9813     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9814     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9815     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9816   }
9817 
9818   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9819   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9820   plex->anchorSection = anchorSection;
9821 
9822   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9823   PetscCall(ISDestroy(&plex->anchorIS));
9824   plex->anchorIS = anchorIS;
9825 
9826   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9827     PetscInt        size, a, pStart, pEnd;
9828     const PetscInt *anchors;
9829 
9830     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9831     PetscCall(ISGetLocalSize(anchorIS, &size));
9832     PetscCall(ISGetIndices(anchorIS, &anchors));
9833     for (a = 0; a < size; a++) {
9834       PetscInt p;
9835 
9836       p = anchors[a];
9837       if (p >= pStart && p < pEnd) {
9838         PetscInt dof;
9839 
9840         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9841         if (dof) {
9842           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9843           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9844         }
9845       }
9846     }
9847     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9848   }
9849   /* reset the generic constraints */
9850   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9851   PetscFunctionReturn(PETSC_SUCCESS);
9852 }
9853 
9854 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9855 {
9856   PetscSection anchorSection;
9857   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9858 
9859   PetscFunctionBegin;
9860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9861   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9862   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9863   PetscCall(PetscSectionGetNumFields(section, &numFields));
9864   if (numFields) {
9865     PetscInt f;
9866     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9867 
9868     for (f = 0; f < numFields; f++) {
9869       PetscInt numComp;
9870 
9871       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9872       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9873     }
9874   }
9875   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9876   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9877   pStart = PetscMax(pStart, sStart);
9878   pEnd   = PetscMin(pEnd, sEnd);
9879   pEnd   = PetscMax(pStart, pEnd);
9880   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9881   for (p = pStart; p < pEnd; p++) {
9882     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9883     if (dof) {
9884       PetscCall(PetscSectionGetDof(section, p, &dof));
9885       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9886       for (f = 0; f < numFields; f++) {
9887         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9888         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9889       }
9890     }
9891   }
9892   PetscCall(PetscSectionSetUp(*cSec));
9893   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9894   PetscFunctionReturn(PETSC_SUCCESS);
9895 }
9896 
9897 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9898 {
9899   PetscSection    aSec;
9900   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9901   const PetscInt *anchors;
9902   PetscInt        numFields, f;
9903   IS              aIS;
9904   MatType         mtype;
9905   PetscBool       iscuda, iskokkos;
9906 
9907   PetscFunctionBegin;
9908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9909   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9910   PetscCall(PetscSectionGetStorageSize(section, &n));
9911   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9912   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9913   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9914   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9915   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9916   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9917   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9918   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9919   else mtype = MATSEQAIJ;
9920   PetscCall(MatSetType(*cMat, mtype));
9921   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9922   PetscCall(ISGetIndices(aIS, &anchors));
9923   /* cSec will be a subset of aSec and section */
9924   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9925   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9926   PetscCall(PetscMalloc1(m + 1, &i));
9927   i[0] = 0;
9928   PetscCall(PetscSectionGetNumFields(section, &numFields));
9929   for (p = pStart; p < pEnd; p++) {
9930     PetscInt rDof, rOff, r;
9931 
9932     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9933     if (!rDof) continue;
9934     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9935     if (numFields) {
9936       for (f = 0; f < numFields; f++) {
9937         annz = 0;
9938         for (r = 0; r < rDof; r++) {
9939           a = anchors[rOff + r];
9940           if (a < sStart || a >= sEnd) continue;
9941           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9942           annz += aDof;
9943         }
9944         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9945         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9946         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9947       }
9948     } else {
9949       annz = 0;
9950       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9951       for (q = 0; q < dof; q++) {
9952         a = anchors[rOff + q];
9953         if (a < sStart || a >= sEnd) continue;
9954         PetscCall(PetscSectionGetDof(section, a, &aDof));
9955         annz += aDof;
9956       }
9957       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9958       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9959       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9960     }
9961   }
9962   nnz = i[m];
9963   PetscCall(PetscMalloc1(nnz, &j));
9964   offset = 0;
9965   for (p = pStart; p < pEnd; p++) {
9966     if (numFields) {
9967       for (f = 0; f < numFields; f++) {
9968         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9969         for (q = 0; q < dof; q++) {
9970           PetscInt rDof, rOff, r;
9971           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9972           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9973           for (r = 0; r < rDof; r++) {
9974             PetscInt s;
9975 
9976             a = anchors[rOff + r];
9977             if (a < sStart || a >= sEnd) continue;
9978             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9979             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9980             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9981           }
9982         }
9983       }
9984     } else {
9985       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9986       for (q = 0; q < dof; q++) {
9987         PetscInt rDof, rOff, r;
9988         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9989         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9990         for (r = 0; r < rDof; r++) {
9991           PetscInt s;
9992 
9993           a = anchors[rOff + r];
9994           if (a < sStart || a >= sEnd) continue;
9995           PetscCall(PetscSectionGetDof(section, a, &aDof));
9996           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9997           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9998         }
9999       }
10000     }
10001   }
10002   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10003   PetscCall(PetscFree(i));
10004   PetscCall(PetscFree(j));
10005   PetscCall(ISRestoreIndices(aIS, &anchors));
10006   PetscFunctionReturn(PETSC_SUCCESS);
10007 }
10008 
10009 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10010 {
10011   DM_Plex     *plex = (DM_Plex *)dm->data;
10012   PetscSection anchorSection, section, cSec;
10013   Mat          cMat;
10014 
10015   PetscFunctionBegin;
10016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10017   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10018   if (anchorSection) {
10019     PetscInt Nf;
10020 
10021     PetscCall(DMGetLocalSection(dm, &section));
10022     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10023     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10024     PetscCall(DMGetNumFields(dm, &Nf));
10025     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10026     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10027     PetscCall(PetscSectionDestroy(&cSec));
10028     PetscCall(MatDestroy(&cMat));
10029   }
10030   PetscFunctionReturn(PETSC_SUCCESS);
10031 }
10032 
10033 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10034 {
10035   IS           subis;
10036   PetscSection section, subsection;
10037 
10038   PetscFunctionBegin;
10039   PetscCall(DMGetLocalSection(dm, &section));
10040   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10041   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10042   /* Create subdomain */
10043   PetscCall(DMPlexFilter(dm, label, value, subdm));
10044   /* Create submodel */
10045   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10046   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10047   PetscCall(DMSetLocalSection(*subdm, subsection));
10048   PetscCall(PetscSectionDestroy(&subsection));
10049   PetscCall(DMCopyDisc(dm, *subdm));
10050   /* Create map from submodel to global model */
10051   if (is) {
10052     PetscSection    sectionGlobal, subsectionGlobal;
10053     IS              spIS;
10054     const PetscInt *spmap;
10055     PetscInt       *subIndices;
10056     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10057     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10058 
10059     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10060     PetscCall(ISGetIndices(spIS, &spmap));
10061     PetscCall(PetscSectionGetNumFields(section, &Nf));
10062     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10063     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10064     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10065     for (p = pStart; p < pEnd; ++p) {
10066       PetscInt gdof, pSubSize = 0;
10067 
10068       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10069       if (gdof > 0) {
10070         for (f = 0; f < Nf; ++f) {
10071           PetscInt fdof, fcdof;
10072 
10073           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10074           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10075           pSubSize += fdof - fcdof;
10076         }
10077         subSize += pSubSize;
10078         if (pSubSize) {
10079           if (bs < 0) {
10080             bs = pSubSize;
10081           } else if (bs != pSubSize) {
10082             /* Layout does not admit a pointwise block size */
10083             bs = 1;
10084           }
10085         }
10086       }
10087     }
10088     /* Must have same blocksize on all procs (some might have no points) */
10089     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10090     bsLocal[1] = bs;
10091     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10092     if (bsMinMax[0] != bsMinMax[1]) {
10093       bs = 1;
10094     } else {
10095       bs = bsMinMax[0];
10096     }
10097     PetscCall(PetscMalloc1(subSize, &subIndices));
10098     for (p = pStart; p < pEnd; ++p) {
10099       PetscInt gdof, goff;
10100 
10101       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10102       if (gdof > 0) {
10103         const PetscInt point = spmap[p];
10104 
10105         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10106         for (f = 0; f < Nf; ++f) {
10107           PetscInt fdof, fcdof, fc, f2, poff = 0;
10108 
10109           /* Can get rid of this loop by storing field information in the global section */
10110           for (f2 = 0; f2 < f; ++f2) {
10111             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10112             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10113             poff += fdof - fcdof;
10114           }
10115           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10116           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10117           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10118         }
10119       }
10120     }
10121     PetscCall(ISRestoreIndices(spIS, &spmap));
10122     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10123     if (bs > 1) {
10124       /* We need to check that the block size does not come from non-contiguous fields */
10125       PetscInt i, j, set = 1;
10126       for (i = 0; i < subSize; i += bs) {
10127         for (j = 0; j < bs; ++j) {
10128           if (subIndices[i + j] != subIndices[i] + j) {
10129             set = 0;
10130             break;
10131           }
10132         }
10133       }
10134       if (set) PetscCall(ISSetBlockSize(*is, bs));
10135     }
10136     /* Attach nullspace */
10137     for (f = 0; f < Nf; ++f) {
10138       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10139       if ((*subdm)->nullspaceConstructors[f]) break;
10140     }
10141     if (f < Nf) {
10142       MatNullSpace nullSpace;
10143       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10144 
10145       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10146       PetscCall(MatNullSpaceDestroy(&nullSpace));
10147     }
10148   }
10149   PetscFunctionReturn(PETSC_SUCCESS);
10150 }
10151 
10152 /*@
10153   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10154 
10155   Input Parameters:
10156 + dm - The `DM`
10157 - dummy - unused argument
10158 
10159   Options Database Key:
10160 . -dm_plex_monitor_throughput - Activate the monitor
10161 
10162   Level: developer
10163 
10164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10165 @*/
10166 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10167 {
10168 #if defined(PETSC_USE_LOG)
10169   PetscStageLog      stageLog;
10170   PetscLogEvent      event;
10171   PetscLogStage      stage;
10172   PetscEventPerfInfo eventInfo;
10173   PetscReal          cellRate, flopRate;
10174   PetscInt           cStart, cEnd, Nf, N;
10175   const char        *name;
10176 #endif
10177 
10178   PetscFunctionBegin;
10179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10180 #if defined(PETSC_USE_LOG)
10181   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10182   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10183   PetscCall(DMGetNumFields(dm, &Nf));
10184   PetscCall(PetscLogGetStageLog(&stageLog));
10185   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10186   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10187   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10188   N        = (cEnd - cStart) * Nf * eventInfo.count;
10189   flopRate = eventInfo.flops / eventInfo.time;
10190   cellRate = N / eventInfo.time;
10191   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)));
10192 #else
10193   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10194 #endif
10195   PetscFunctionReturn(PETSC_SUCCESS);
10196 }
10197