xref: /petsc/src/dm/impls/plex/plex.c (revision 9de6b0b6e81799bfdfbece7f1f5e07a4003f5640)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm     - The `DMPLEX` object
157 . n      - The number of vectors
158 . u      - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1633     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1641     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm        - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer    - The `PetscViewer` for saving
1954 - sectiondm - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   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   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) PetscAssertPointer(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) PetscAssertPointer(globalDofSF, 5);
2346   if (localDofSF) PetscAssertPointer(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   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   Calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(PetscFree(mesh->cellTypes));
2537   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2538   PetscCall(PetscFree(mesh->tetgenOpts));
2539   PetscCall(PetscFree(mesh->triangleOpts));
2540   PetscCall(PetscFree(mesh->transformType));
2541   PetscCall(PetscFree(mesh->distributionName));
2542   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2543   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2544   PetscCall(ISDestroy(&mesh->subpointIS));
2545   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2546   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2548   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2549   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2550   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2551   PetscCall(ISDestroy(&mesh->anchorIS));
2552   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2553   PetscCall(PetscFree(mesh->parents));
2554   PetscCall(PetscFree(mesh->childIDs));
2555   PetscCall(PetscSectionDestroy(&mesh->childSection));
2556   PetscCall(PetscFree(mesh->children));
2557   PetscCall(DMDestroy(&mesh->referenceTree));
2558   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2559   PetscCall(PetscFree(mesh->neighbors));
2560   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2561   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2562   PetscCall(PetscFree(mesh));
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2567 {
2568   PetscSection           sectionGlobal;
2569   PetscInt               bs = -1, mbs;
2570   PetscInt               localSize, localStart = 0;
2571   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2572   MatType                mtype;
2573   ISLocalToGlobalMapping ltog;
2574 
2575   PetscFunctionBegin;
2576   PetscCall(MatInitializePackage());
2577   mtype = dm->mattype;
2578   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2579   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2580   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2581   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2582   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2583   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2584   PetscCall(MatSetType(*J, mtype));
2585   PetscCall(MatSetFromOptions(*J));
2586   PetscCall(MatGetBlockSize(*J, &mbs));
2587   if (mbs > 1) bs = mbs;
2588   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2589   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2590   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2591   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2593   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2594   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2595   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2596   if (!isShell) {
2597     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2598     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2599     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2600 
2601     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2602 
2603     PetscCall(PetscCalloc1(localSize, &pblocks));
2604     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2605     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2606     for (p = pStart; p < pEnd; ++p) {
2607       switch (dm->blocking_type) {
2608       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2609         PetscInt bdof, offset;
2610 
2611         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2612         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2613         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2614         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2615         dof  = dof < 0 ? -(dof + 1) : dof;
2616         bdof = cdof && (dof - cdof) ? 1 : dof;
2617         if (dof) {
2618           if (bs < 0) {
2619             bs = bdof;
2620           } else if (bs != bdof) {
2621             bs = 1;
2622           }
2623         }
2624       } break;
2625       case DM_BLOCKING_FIELD_NODE: {
2626         for (PetscInt field = 0; field < num_fields; field++) {
2627           PetscInt num_comp, bdof, offset;
2628           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2629           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2630           if (dof < 0) continue;
2631           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2632           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2633           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2634           PetscInt num_nodes = dof / num_comp;
2635           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2636           // Handle possibly constant block size (unlikely)
2637           bdof = cdof && (dof - cdof) ? 1 : dof;
2638           if (dof) {
2639             if (bs < 0) {
2640               bs = bdof;
2641             } else if (bs != bdof) {
2642               bs = 1;
2643             }
2644           }
2645         }
2646       } break;
2647       }
2648     }
2649     /* Must have same blocksize on all procs (some might have no points) */
2650     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2651     bsLocal[1] = bs;
2652     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2653     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2654     else bs = bsMinMax[0];
2655     bs = PetscMax(1, bs);
2656     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2657     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2658       PetscCall(MatSetBlockSize(*J, bs));
2659       PetscCall(MatSetUp(*J));
2660     } else {
2661       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2662       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2663       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2664     }
2665     { // Consolidate blocks
2666       PetscInt nblocks = 0;
2667       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2668         if (pblocks[i] == 0) continue;
2669         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2670         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2671       }
2672       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2673     }
2674     PetscCall(PetscFree(pblocks));
2675   }
2676   PetscCall(MatSetDM(*J, dm));
2677   PetscFunctionReturn(PETSC_SUCCESS);
2678 }
2679 
2680 /*@
2681   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . dm - The `DMPLEX`
2687 
2688   Output Parameter:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . dm - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + dm     - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2754 @*/
2755 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2756 {
2757   DM_Plex *mesh = (DM_Plex *)dm->data;
2758 
2759   PetscFunctionBegin;
2760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2761   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2762   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2763   PetscCall(PetscFree(mesh->cellTypes));
2764   PetscFunctionReturn(PETSC_SUCCESS);
2765 }
2766 
2767 /*@
2768   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2769 
2770   Not Collective
2771 
2772   Input Parameters:
2773 + dm - The `DMPLEX`
2774 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2775 
2776   Output Parameter:
2777 . size - The cone size for point `p`
2778 
2779   Level: beginner
2780 
2781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2782 @*/
2783 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2784 {
2785   DM_Plex *mesh = (DM_Plex *)dm->data;
2786 
2787   PetscFunctionBegin;
2788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2789   PetscAssertPointer(size, 3);
2790   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2791   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2792   PetscFunctionReturn(PETSC_SUCCESS);
2793 }
2794 
2795 /*@
2796   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2797 
2798   Not Collective
2799 
2800   Input Parameters:
2801 + dm   - The `DMPLEX`
2802 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2803 - size - The cone size for point `p`
2804 
2805   Level: beginner
2806 
2807   Note:
2808   This should be called after `DMPlexSetChart()`.
2809 
2810 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2811 @*/
2812 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2813 {
2814   DM_Plex *mesh = (DM_Plex *)dm->data;
2815 
2816   PetscFunctionBegin;
2817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2818   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2819   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2820   PetscFunctionReturn(PETSC_SUCCESS);
2821 }
2822 
2823 /*@C
2824   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2825 
2826   Not Collective
2827 
2828   Input Parameters:
2829 + dm - The `DMPLEX`
2830 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2831 
2832   Output Parameter:
2833 . cone - An array of points which are on the in-edges for point `p`
2834 
2835   Level: beginner
2836 
2837   Fortran Notes:
2838   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2839   `DMPlexRestoreCone()` is not needed/available in C.
2840 
2841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2842 @*/
2843 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2844 {
2845   DM_Plex *mesh = (DM_Plex *)dm->data;
2846   PetscInt off;
2847 
2848   PetscFunctionBegin;
2849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2850   PetscAssertPointer(cone, 3);
2851   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2852   *cone = &mesh->cones[off];
2853   PetscFunctionReturn(PETSC_SUCCESS);
2854 }
2855 
2856 /*@C
2857   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2858 
2859   Not Collective
2860 
2861   Input Parameters:
2862 + dm - The `DMPLEX`
2863 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2864 
2865   Output Parameters:
2866 + pConesSection - `PetscSection` describing the layout of `pCones`
2867 - pCones        - An array of points which are on the in-edges for the point set `p`
2868 
2869   Level: intermediate
2870 
2871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2872 @*/
2873 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2874 {
2875   PetscSection cs, newcs;
2876   PetscInt    *cones;
2877   PetscInt    *newarr = NULL;
2878   PetscInt     n;
2879 
2880   PetscFunctionBegin;
2881   PetscCall(DMPlexGetCones(dm, &cones));
2882   PetscCall(DMPlexGetConeSection(dm, &cs));
2883   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2884   if (pConesSection) *pConesSection = newcs;
2885   if (pCones) {
2886     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2887     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2888   }
2889   PetscFunctionReturn(PETSC_SUCCESS);
2890 }
2891 
2892 /*@
2893   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2894 
2895   Not Collective
2896 
2897   Input Parameters:
2898 + dm     - The `DMPLEX`
2899 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2900 
2901   Output Parameter:
2902 . expandedPoints - An array of vertices recursively expanded from input points
2903 
2904   Level: advanced
2905 
2906   Notes:
2907   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2908 
2909   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2910 
2911 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2912           `DMPlexGetDepth()`, `IS`
2913 @*/
2914 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2915 {
2916   IS      *expandedPointsAll;
2917   PetscInt depth;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2922   PetscAssertPointer(expandedPoints, 3);
2923   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2924   *expandedPoints = expandedPointsAll[0];
2925   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2926   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2927   PetscFunctionReturn(PETSC_SUCCESS);
2928 }
2929 
2930 /*@
2931   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2932 
2933   Not Collective
2934 
2935   Input Parameters:
2936 + dm     - The `DMPLEX`
2937 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2938 
2939   Output Parameters:
2940 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2941 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2942 - sections       - (optional) An array of sections which describe mappings from points to their cone points
2943 
2944   Level: advanced
2945 
2946   Notes:
2947   Like `DMPlexGetConeTuple()` but recursive.
2948 
2949   Array `expandedPoints` has size equal to `depth`. `Each expandedPoints`[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
2950   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2951 
2952   Array section has size equal to depth.  Each `PetscSection` sections[d] realizes mapping from `expandedPoints`[d+1] (section points) to `expandedPoints`[d] (section dofs) as follows:
2953   (1) DAG points in expandedPoints[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2954   (2) DAG points in expandedPoints[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2955 
2956 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2957           `DMPlexGetDepth()`, `PetscSection`, `IS`
2958 @*/
2959 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2960 {
2961   const PetscInt *arr0 = NULL, *cone = NULL;
2962   PetscInt       *arr = NULL, *newarr = NULL;
2963   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2964   IS             *expandedPoints_;
2965   PetscSection   *sections_;
2966 
2967   PetscFunctionBegin;
2968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2969   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2970   if (depth) PetscAssertPointer(depth, 3);
2971   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
2972   if (sections) PetscAssertPointer(sections, 5);
2973   PetscCall(ISGetLocalSize(points, &n));
2974   PetscCall(ISGetIndices(points, &arr0));
2975   PetscCall(DMPlexGetDepth(dm, &depth_));
2976   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2977   PetscCall(PetscCalloc1(depth_, &sections_));
2978   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2979   for (d = depth_ - 1; d >= 0; d--) {
2980     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2981     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2982     for (i = 0; i < n; i++) {
2983       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2984       if (arr[i] >= start && arr[i] < end) {
2985         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2986         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2987       } else {
2988         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2989       }
2990     }
2991     PetscCall(PetscSectionSetUp(sections_[d]));
2992     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2993     PetscCall(PetscMalloc1(newn, &newarr));
2994     for (i = 0; i < n; i++) {
2995       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2996       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2997       if (cn > 1) {
2998         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2999         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3000       } else {
3001         newarr[co] = arr[i];
3002       }
3003     }
3004     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3005     arr = newarr;
3006     n   = newn;
3007   }
3008   PetscCall(ISRestoreIndices(points, &arr0));
3009   *depth = depth_;
3010   if (expandedPoints) *expandedPoints = expandedPoints_;
3011   else {
3012     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3013     PetscCall(PetscFree(expandedPoints_));
3014   }
3015   if (sections) *sections = sections_;
3016   else {
3017     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3018     PetscCall(PetscFree(sections_));
3019   }
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@
3024   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm     - The `DMPLEX`
3030 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3031 
3032   Output Parameters:
3033 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3034 . expandedPoints - (optional) An array of recursively expanded cones
3035 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3036 
3037   Level: advanced
3038 
3039   Note:
3040   See `DMPlexGetConeRecursive()`
3041 
3042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3043           `DMPlexGetDepth()`, `IS`, `PetscSection`
3044 @*/
3045 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3046 {
3047   PetscInt d, depth_;
3048 
3049   PetscFunctionBegin;
3050   PetscCall(DMPlexGetDepth(dm, &depth_));
3051   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3052   if (depth) *depth = 0;
3053   if (expandedPoints) {
3054     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3055     PetscCall(PetscFree(*expandedPoints));
3056   }
3057   if (sections) {
3058     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3059     PetscCall(PetscFree(*sections));
3060   }
3061   PetscFunctionReturn(PETSC_SUCCESS);
3062 }
3063 
3064 /*@
3065   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3066 
3067   Not Collective
3068 
3069   Input Parameters:
3070 + dm   - The `DMPLEX`
3071 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3072 - cone - An array of points which are on the in-edges for point `p`
3073 
3074   Level: beginner
3075 
3076   Note:
3077   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3078 
3079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3080 @*/
3081 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3082 {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084   PetscInt dof, off, c;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3089   if (dof) PetscAssertPointer(cone, 3);
3090   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3091   if (PetscDefined(USE_DEBUG)) {
3092     PetscInt pStart, pEnd;
3093     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3094     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3095     for (c = 0; c < dof; ++c) {
3096       PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3097       mesh->cones[off + c] = cone[c];
3098     }
3099   } else {
3100     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3101   }
3102   PetscFunctionReturn(PETSC_SUCCESS);
3103 }
3104 
3105 /*@C
3106   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3107 
3108   Not Collective
3109 
3110   Input Parameters:
3111 + dm - The `DMPLEX`
3112 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3113 
3114   Output Parameter:
3115 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3116                     integer giving the prescription for cone traversal.
3117 
3118   Level: beginner
3119 
3120   Note:
3121   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3122   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3123   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3124   with the identity.
3125 
3126   Fortran Notes:
3127   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3128   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3129 
3130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3131 @*/
3132 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3133 {
3134   DM_Plex *mesh = (DM_Plex *)dm->data;
3135   PetscInt off;
3136 
3137   PetscFunctionBegin;
3138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3139   if (PetscDefined(USE_DEBUG)) {
3140     PetscInt dof;
3141     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3142     if (dof) PetscAssertPointer(coneOrientation, 3);
3143   }
3144   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3145 
3146   *coneOrientation = &mesh->coneOrientations[off];
3147   PetscFunctionReturn(PETSC_SUCCESS);
3148 }
3149 
3150 /*@
3151   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3152 
3153   Not Collective
3154 
3155   Input Parameters:
3156 + dm              - The `DMPLEX`
3157 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3158 - coneOrientation - An array of orientations
3159 
3160   Level: beginner
3161 
3162   Notes:
3163   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3164 
3165   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3166 
3167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3168 @*/
3169 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3170 {
3171   DM_Plex *mesh = (DM_Plex *)dm->data;
3172   PetscInt pStart, pEnd;
3173   PetscInt dof, off, c;
3174 
3175   PetscFunctionBegin;
3176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3177   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3178   if (dof) PetscAssertPointer(coneOrientation, 3);
3179   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3180   if (PetscDefined(USE_DEBUG)) {
3181     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3182     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3183     for (c = 0; c < dof; ++c) {
3184       PetscInt cdof, o = coneOrientation[c];
3185 
3186       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3187       PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3188       mesh->coneOrientations[off + c] = o;
3189     }
3190   } else {
3191     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3192   }
3193   PetscFunctionReturn(PETSC_SUCCESS);
3194 }
3195 
3196 /*@
3197   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3198 
3199   Not Collective
3200 
3201   Input Parameters:
3202 + dm        - The `DMPLEX`
3203 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3204 . conePos   - The local index in the cone where the point should be put
3205 - conePoint - The mesh point to insert
3206 
3207   Level: beginner
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3210 @*/
3211 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt pStart, pEnd;
3215   PetscInt dof, off;
3216 
3217   PetscFunctionBegin;
3218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3219   if (PetscDefined(USE_DEBUG)) {
3220     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3221     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3222     PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3223     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3224     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3225   }
3226   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3227   mesh->cones[off + conePos] = conePoint;
3228   PetscFunctionReturn(PETSC_SUCCESS);
3229 }
3230 
3231 /*@
3232   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3233 
3234   Not Collective
3235 
3236   Input Parameters:
3237 + dm              - The `DMPLEX`
3238 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3239 . conePos         - The local index in the cone where the point should be put
3240 - coneOrientation - The point orientation to insert
3241 
3242   Level: beginner
3243 
3244   Note:
3245   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3246 
3247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3248 @*/
3249 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3250 {
3251   DM_Plex *mesh = (DM_Plex *)dm->data;
3252   PetscInt pStart, pEnd;
3253   PetscInt dof, off;
3254 
3255   PetscFunctionBegin;
3256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3257   if (PetscDefined(USE_DEBUG)) {
3258     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3259     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3260     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3261     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3262   }
3263   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3264   mesh->coneOrientations[off + conePos] = coneOrientation;
3265   PetscFunctionReturn(PETSC_SUCCESS);
3266 }
3267 
3268 /*@C
3269   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3270 
3271   Not collective
3272 
3273   Input Parameters:
3274 + dm - The DMPlex
3275 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3276 
3277   Output Parameters:
3278 + cone - An array of points which are on the in-edges for point `p`
3279 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3280         integer giving the prescription for cone traversal.
3281 
3282   Level: beginner
3283 
3284   Notes:
3285   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3286   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3287   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3288   with the identity.
3289 
3290   Fortran Notes:
3291   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3292   `DMPlexRestoreCone()` is not needed/available in C.
3293 
3294 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3295 @*/
3296 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3297 {
3298   DM_Plex *mesh = (DM_Plex *)dm->data;
3299 
3300   PetscFunctionBegin;
3301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3302   if (mesh->tr) {
3303     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3304   } else {
3305     PetscInt off;
3306     if (PetscDefined(USE_DEBUG)) {
3307       PetscInt dof;
3308       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3309       if (dof) {
3310         if (cone) PetscAssertPointer(cone, 3);
3311         if (ornt) PetscAssertPointer(ornt, 4);
3312       }
3313     }
3314     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3315     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3316     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3317   }
3318   PetscFunctionReturn(PETSC_SUCCESS);
3319 }
3320 
3321 /*@C
3322   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3323 
3324   Not Collective
3325 
3326   Input Parameters:
3327 + dm   - The DMPlex
3328 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3329 . cone - An array of points which are on the in-edges for point p
3330 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3331         integer giving the prescription for cone traversal.
3332 
3333   Level: beginner
3334 
3335   Notes:
3336   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3337   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3338   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3339   with the identity.
3340 
3341   Fortran Notes:
3342   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3343   `DMPlexRestoreCone()` is not needed/available in C.
3344 
3345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3346 @*/
3347 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3348 {
3349   DM_Plex *mesh = (DM_Plex *)dm->data;
3350 
3351   PetscFunctionBegin;
3352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3353   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3354   PetscFunctionReturn(PETSC_SUCCESS);
3355 }
3356 
3357 /*@
3358   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + dm - The `DMPLEX`
3364 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 
3366   Output Parameter:
3367 . size - The support size for point `p`
3368 
3369   Level: beginner
3370 
3371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3372 @*/
3373 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3374 {
3375   DM_Plex *mesh = (DM_Plex *)dm->data;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   PetscAssertPointer(size, 3);
3380   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(PETSC_SUCCESS);
3382 }
3383 
3384 /*@
3385   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + dm   - The `DMPLEX`
3391 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 - size - The support size for point `p`
3393 
3394   Level: beginner
3395 
3396   Note:
3397   This should be called after `DMPlexSetChart()`.
3398 
3399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3400 @*/
3401 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3402 {
3403   DM_Plex *mesh = (DM_Plex *)dm->data;
3404 
3405   PetscFunctionBegin;
3406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3407   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3408   PetscFunctionReturn(PETSC_SUCCESS);
3409 }
3410 
3411 /*@C
3412   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3413 
3414   Not Collective
3415 
3416   Input Parameters:
3417 + dm - The `DMPLEX`
3418 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3419 
3420   Output Parameter:
3421 . support - An array of points which are on the out-edges for point `p`
3422 
3423   Level: beginner
3424 
3425   Fortran Notes:
3426   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3427   `DMPlexRestoreSupport()` is not needed/available in C.
3428 
3429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3430 @*/
3431 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3432 {
3433   DM_Plex *mesh = (DM_Plex *)dm->data;
3434   PetscInt off;
3435 
3436   PetscFunctionBegin;
3437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3438   PetscAssertPointer(support, 3);
3439   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3440   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3441   PetscFunctionReturn(PETSC_SUCCESS);
3442 }
3443 
3444 /*@
3445   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3446 
3447   Not Collective
3448 
3449   Input Parameters:
3450 + dm      - The `DMPLEX`
3451 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3452 - support - An array of points which are on the out-edges for point `p`
3453 
3454   Level: beginner
3455 
3456   Note:
3457   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3458 
3459 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3460 @*/
3461 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3462 {
3463   DM_Plex *mesh = (DM_Plex *)dm->data;
3464   PetscInt pStart, pEnd;
3465   PetscInt dof, off, c;
3466 
3467   PetscFunctionBegin;
3468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3469   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3470   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3471   if (dof) PetscAssertPointer(support, 3);
3472   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3473   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3474   for (c = 0; c < dof; ++c) {
3475     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3476     mesh->supports[off + c] = support[c];
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@
3482   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + dm           - The `DMPLEX`
3488 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . supportPos   - The local index in the cone where the point should be put
3490 - supportPoint - The mesh point to insert
3491 
3492   Level: beginner
3493 
3494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3495 @*/
3496 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3497 {
3498   DM_Plex *mesh = (DM_Plex *)dm->data;
3499   PetscInt pStart, pEnd;
3500   PetscInt dof, off;
3501 
3502   PetscFunctionBegin;
3503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3504   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3505   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3506   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3507   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3508   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3509   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3510   mesh->supports[off + supportPos] = supportPoint;
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3515 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3516 {
3517   switch (ct) {
3518   case DM_POLYTOPE_SEGMENT:
3519     if (o == -1) return -2;
3520     break;
3521   case DM_POLYTOPE_TRIANGLE:
3522     if (o == -3) return -1;
3523     if (o == -2) return -3;
3524     if (o == -1) return -2;
3525     break;
3526   case DM_POLYTOPE_QUADRILATERAL:
3527     if (o == -4) return -2;
3528     if (o == -3) return -1;
3529     if (o == -2) return -4;
3530     if (o == -1) return -3;
3531     break;
3532   default:
3533     return o;
3534   }
3535   return o;
3536 }
3537 
3538 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3539 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3540 {
3541   switch (ct) {
3542   case DM_POLYTOPE_SEGMENT:
3543     if ((o == -2) || (o == 1)) return -1;
3544     if (o == -1) return 0;
3545     break;
3546   case DM_POLYTOPE_TRIANGLE:
3547     if (o == -3) return -2;
3548     if (o == -2) return -1;
3549     if (o == -1) return -3;
3550     break;
3551   case DM_POLYTOPE_QUADRILATERAL:
3552     if (o == -4) return -2;
3553     if (o == -3) return -1;
3554     if (o == -2) return -4;
3555     if (o == -1) return -3;
3556     break;
3557   default:
3558     return o;
3559   }
3560   return o;
3561 }
3562 
3563 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3564 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3565 {
3566   PetscInt pStart, pEnd, p;
3567 
3568   PetscFunctionBegin;
3569   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3570   for (p = pStart; p < pEnd; ++p) {
3571     const PetscInt *cone, *ornt;
3572     PetscInt        coneSize, c;
3573 
3574     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3575     PetscCall(DMPlexGetCone(dm, p, &cone));
3576     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3577     for (c = 0; c < coneSize; ++c) {
3578       DMPolytopeType ct;
3579       const PetscInt o = ornt[c];
3580 
3581       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3582       switch (ct) {
3583       case DM_POLYTOPE_SEGMENT:
3584         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3585         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3586         break;
3587       case DM_POLYTOPE_TRIANGLE:
3588         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3589         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3590         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3591         break;
3592       case DM_POLYTOPE_QUADRILATERAL:
3593         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3594         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3595         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3596         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3597         break;
3598       default:
3599         break;
3600       }
3601     }
3602   }
3603   PetscFunctionReturn(PETSC_SUCCESS);
3604 }
3605 
3606 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3607 {
3608   DM_Plex *mesh = (DM_Plex *)dm->data;
3609 
3610   PetscFunctionBeginHot;
3611   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3612     if (useCone) {
3613       PetscCall(DMPlexGetConeSize(dm, p, size));
3614       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3615     } else {
3616       PetscCall(DMPlexGetSupportSize(dm, p, size));
3617       PetscCall(DMPlexGetSupport(dm, p, arr));
3618     }
3619   } else {
3620     if (useCone) {
3621       const PetscSection s   = mesh->coneSection;
3622       const PetscInt     ps  = p - s->pStart;
3623       const PetscInt     off = s->atlasOff[ps];
3624 
3625       *size = s->atlasDof[ps];
3626       *arr  = mesh->cones + off;
3627       *ornt = mesh->coneOrientations + off;
3628     } else {
3629       const PetscSection s   = mesh->supportSection;
3630       const PetscInt     ps  = p - s->pStart;
3631       const PetscInt     off = s->atlasOff[ps];
3632 
3633       *size = s->atlasDof[ps];
3634       *arr  = mesh->supports + off;
3635     }
3636   }
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3641 {
3642   DM_Plex *mesh = (DM_Plex *)dm->data;
3643 
3644   PetscFunctionBeginHot;
3645   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3646     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3647   }
3648   PetscFunctionReturn(PETSC_SUCCESS);
3649 }
3650 
3651 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3652 {
3653   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3654   PetscInt       *closure;
3655   const PetscInt *tmp = NULL, *tmpO = NULL;
3656   PetscInt        off = 0, tmpSize, t;
3657 
3658   PetscFunctionBeginHot;
3659   if (ornt) {
3660     PetscCall(DMPlexGetCellType(dm, p, &ct));
3661     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3662   }
3663   if (*points) {
3664     closure = *points;
3665   } else {
3666     PetscInt maxConeSize, maxSupportSize;
3667     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3668     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3669   }
3670   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3671   if (ct == DM_POLYTOPE_UNKNOWN) {
3672     closure[off++] = p;
3673     closure[off++] = 0;
3674     for (t = 0; t < tmpSize; ++t) {
3675       closure[off++] = tmp[t];
3676       closure[off++] = tmpO ? tmpO[t] : 0;
3677     }
3678   } else {
3679     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3680 
3681     /* We assume that cells with a valid type have faces with a valid type */
3682     closure[off++] = p;
3683     closure[off++] = ornt;
3684     for (t = 0; t < tmpSize; ++t) {
3685       DMPolytopeType ft;
3686 
3687       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3688       closure[off++] = tmp[arr[t]];
3689       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3690     }
3691   }
3692   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3693   if (numPoints) *numPoints = tmpSize + 1;
3694   if (points) *points = closure;
3695   PetscFunctionReturn(PETSC_SUCCESS);
3696 }
3697 
3698 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3699 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3700 {
3701   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3702   const PetscInt *cone, *ornt;
3703   PetscInt       *pts, *closure = NULL;
3704   DMPolytopeType  ft;
3705   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3706   PetscInt        dim, coneSize, c, d, clSize, cl;
3707 
3708   PetscFunctionBeginHot;
3709   PetscCall(DMGetDimension(dm, &dim));
3710   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3711   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3712   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3713   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3714   maxSize       = PetscMax(coneSeries, supportSeries);
3715   if (*points) {
3716     pts = *points;
3717   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3718   c        = 0;
3719   pts[c++] = point;
3720   pts[c++] = o;
3721   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3722   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3723   for (cl = 0; cl < clSize * 2; cl += 2) {
3724     pts[c++] = closure[cl];
3725     pts[c++] = closure[cl + 1];
3726   }
3727   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3728   for (cl = 0; cl < clSize * 2; cl += 2) {
3729     pts[c++] = closure[cl];
3730     pts[c++] = closure[cl + 1];
3731   }
3732   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3733   for (d = 2; d < coneSize; ++d) {
3734     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3735     pts[c++] = cone[arr[d * 2 + 0]];
3736     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3737   }
3738   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3739   if (dim >= 3) {
3740     for (d = 2; d < coneSize; ++d) {
3741       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3742       const PetscInt *fcone, *fornt;
3743       PetscInt        fconeSize, fc, i;
3744 
3745       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3746       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3747       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3748       for (fc = 0; fc < fconeSize; ++fc) {
3749         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3750         const PetscInt co = farr[fc * 2 + 1];
3751 
3752         for (i = 0; i < c; i += 2)
3753           if (pts[i] == cp) break;
3754         if (i == c) {
3755           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3756           pts[c++] = cp;
3757           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3758         }
3759       }
3760       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3761     }
3762   }
3763   *numPoints = c / 2;
3764   *points    = pts;
3765   PetscFunctionReturn(PETSC_SUCCESS);
3766 }
3767 
3768 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3769 {
3770   DMPolytopeType ct;
3771   PetscInt      *closure, *fifo;
3772   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3773   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3774   PetscInt       depth, maxSize;
3775 
3776   PetscFunctionBeginHot;
3777   PetscCall(DMPlexGetDepth(dm, &depth));
3778   if (depth == 1) {
3779     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3780     PetscFunctionReturn(PETSC_SUCCESS);
3781   }
3782   PetscCall(DMPlexGetCellType(dm, p, &ct));
3783   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3784   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3785     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3786     PetscFunctionReturn(PETSC_SUCCESS);
3787   }
3788   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3789   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3790   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3791   maxSize       = PetscMax(coneSeries, supportSeries);
3792   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3793   if (*points) {
3794     closure = *points;
3795   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3796   closure[closureSize++] = p;
3797   closure[closureSize++] = ornt;
3798   fifo[fifoSize++]       = p;
3799   fifo[fifoSize++]       = ornt;
3800   fifo[fifoSize++]       = ct;
3801   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3802   while (fifoSize - fifoStart) {
3803     const PetscInt       q    = fifo[fifoStart++];
3804     const PetscInt       o    = fifo[fifoStart++];
3805     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3806     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3807     const PetscInt      *tmp, *tmpO = NULL;
3808     PetscInt             tmpSize, t;
3809 
3810     if (PetscDefined(USE_DEBUG)) {
3811       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3812       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3813     }
3814     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3815     for (t = 0; t < tmpSize; ++t) {
3816       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3817       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3818       const PetscInt cp = tmp[ip];
3819       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3820       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3821       PetscInt       c;
3822 
3823       /* Check for duplicate */
3824       for (c = 0; c < closureSize; c += 2) {
3825         if (closure[c] == cp) break;
3826       }
3827       if (c == closureSize) {
3828         closure[closureSize++] = cp;
3829         closure[closureSize++] = co;
3830         fifo[fifoSize++]       = cp;
3831         fifo[fifoSize++]       = co;
3832         fifo[fifoSize++]       = ct;
3833       }
3834     }
3835     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3836   }
3837   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3838   if (numPoints) *numPoints = closureSize / 2;
3839   if (points) *points = closure;
3840   PetscFunctionReturn(PETSC_SUCCESS);
3841 }
3842 
3843 /*@C
3844   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3845 
3846   Not Collective
3847 
3848   Input Parameters:
3849 + dm      - The `DMPLEX`
3850 . p       - The mesh point
3851 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3852 
3853   Input/Output Parameter:
3854 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3855            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3856 
3857   Output Parameter:
3858 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3859 
3860   Level: beginner
3861 
3862   Note:
3863   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3864 
3865   Fortran Notes:
3866   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3867 
3868 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3869 @*/
3870 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3871 {
3872   PetscFunctionBeginHot;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   if (numPoints) PetscAssertPointer(numPoints, 4);
3875   if (points) PetscAssertPointer(points, 5);
3876   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3877   PetscFunctionReturn(PETSC_SUCCESS);
3878 }
3879 
3880 /*@C
3881   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3882 
3883   Not Collective
3884 
3885   Input Parameters:
3886 + dm        - The `DMPLEX`
3887 . p         - The mesh point
3888 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3889 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3890 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3891 
3892   Level: beginner
3893 
3894   Note:
3895   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3896 
3897 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3898 @*/
3899 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3900 {
3901   PetscFunctionBeginHot;
3902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3903   if (numPoints) *numPoints = 0;
3904   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3905   PetscFunctionReturn(PETSC_SUCCESS);
3906 }
3907 
3908 /*@
3909   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3910 
3911   Not Collective
3912 
3913   Input Parameter:
3914 . dm - The `DMPLEX`
3915 
3916   Output Parameters:
3917 + maxConeSize    - The maximum number of in-edges
3918 - maxSupportSize - The maximum number of out-edges
3919 
3920   Level: beginner
3921 
3922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3923 @*/
3924 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3925 {
3926   DM_Plex *mesh = (DM_Plex *)dm->data;
3927 
3928   PetscFunctionBegin;
3929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3930   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3931   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3932   PetscFunctionReturn(PETSC_SUCCESS);
3933 }
3934 
3935 PetscErrorCode DMSetUp_Plex(DM dm)
3936 {
3937   DM_Plex *mesh = (DM_Plex *)dm->data;
3938   PetscInt size, maxSupportSize;
3939 
3940   PetscFunctionBegin;
3941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3942   PetscCall(PetscSectionSetUp(mesh->coneSection));
3943   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3944   PetscCall(PetscMalloc1(size, &mesh->cones));
3945   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3946   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3947   if (maxSupportSize) {
3948     PetscCall(PetscSectionSetUp(mesh->supportSection));
3949     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3950     PetscCall(PetscMalloc1(size, &mesh->supports));
3951   }
3952   PetscFunctionReturn(PETSC_SUCCESS);
3953 }
3954 
3955 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3956 {
3957   PetscFunctionBegin;
3958   if (subdm) PetscCall(DMClone(dm, subdm));
3959   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3960   if (subdm) (*subdm)->useNatural = dm->useNatural;
3961   if (dm->useNatural && dm->sfMigration) {
3962     PetscSF sfNatural;
3963 
3964     (*subdm)->sfMigration = dm->sfMigration;
3965     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3966     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3967     (*subdm)->sfNatural = sfNatural;
3968   }
3969   PetscFunctionReturn(PETSC_SUCCESS);
3970 }
3971 
3972 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3973 {
3974   PetscInt i = 0;
3975 
3976   PetscFunctionBegin;
3977   PetscCall(DMClone(dms[0], superdm));
3978   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3979   (*superdm)->useNatural = PETSC_FALSE;
3980   for (i = 0; i < len; i++) {
3981     if (dms[i]->useNatural && dms[i]->sfMigration) {
3982       PetscSF sfNatural;
3983 
3984       (*superdm)->sfMigration = dms[i]->sfMigration;
3985       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3986       (*superdm)->useNatural = PETSC_TRUE;
3987       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3988       (*superdm)->sfNatural = sfNatural;
3989       break;
3990     }
3991   }
3992   PetscFunctionReturn(PETSC_SUCCESS);
3993 }
3994 
3995 /*@
3996   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3997 
3998   Not Collective
3999 
4000   Input Parameter:
4001 . dm - The `DMPLEX`
4002 
4003   Level: beginner
4004 
4005   Note:
4006   This should be called after all calls to `DMPlexSetCone()`
4007 
4008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4009 @*/
4010 PetscErrorCode DMPlexSymmetrize(DM dm)
4011 {
4012   DM_Plex  *mesh = (DM_Plex *)dm->data;
4013   PetscInt *offsets;
4014   PetscInt  supportSize;
4015   PetscInt  pStart, pEnd, p;
4016 
4017   PetscFunctionBegin;
4018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4019   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4020   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4021   /* Calculate support sizes */
4022   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4023   for (p = pStart; p < pEnd; ++p) {
4024     PetscInt dof, off, c;
4025 
4026     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4027     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4028     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4029   }
4030   PetscCall(PetscSectionSetUp(mesh->supportSection));
4031   /* Calculate supports */
4032   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4033   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4034   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4035   for (p = pStart; p < pEnd; ++p) {
4036     PetscInt dof, off, c;
4037 
4038     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4039     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4040     for (c = off; c < off + dof; ++c) {
4041       const PetscInt q = mesh->cones[c];
4042       PetscInt       offS;
4043 
4044       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4045 
4046       mesh->supports[offS + offsets[q]] = p;
4047       ++offsets[q];
4048     }
4049   }
4050   PetscCall(PetscFree(offsets));
4051   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4052   PetscFunctionReturn(PETSC_SUCCESS);
4053 }
4054 
4055 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4056 {
4057   IS stratumIS;
4058 
4059   PetscFunctionBegin;
4060   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4061   if (PetscDefined(USE_DEBUG)) {
4062     PetscInt  qStart, qEnd, numLevels, level;
4063     PetscBool overlap = PETSC_FALSE;
4064     PetscCall(DMLabelGetNumValues(label, &numLevels));
4065     for (level = 0; level < numLevels; level++) {
4066       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4067       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4068         overlap = PETSC_TRUE;
4069         break;
4070       }
4071     }
4072     PetscCheck(!overlap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
4073   }
4074   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4075   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4076   PetscCall(ISDestroy(&stratumIS));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@
4081   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4082   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4083   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4084   the DAG.
4085 
4086   Collective
4087 
4088   Input Parameter:
4089 . dm - The `DMPLEX`
4090 
4091   Level: beginner
4092 
4093   Notes:
4094   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4095   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4096   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4097   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4098   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4099 
4100   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4101   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4102   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
4103   to interpolate only that one (e0), so that
4104 .vb
4105   cone(c0) = {e0, v2}
4106   cone(e0) = {v0, v1}
4107 .ve
4108   If `DMPlexStratify()` is run on this mesh, it will give depths
4109 .vb
4110    depth 0 = {v0, v1, v2}
4111    depth 1 = {e0, c0}
4112 .ve
4113   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4114 
4115   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4116 
4117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4118 @*/
4119 PetscErrorCode DMPlexStratify(DM dm)
4120 {
4121   DM_Plex *mesh = (DM_Plex *)dm->data;
4122   DMLabel  label;
4123   PetscInt pStart, pEnd, p;
4124   PetscInt numRoots = 0, numLeaves = 0;
4125 
4126   PetscFunctionBegin;
4127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4128   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4129 
4130   /* Create depth label */
4131   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4132   PetscCall(DMCreateLabel(dm, "depth"));
4133   PetscCall(DMPlexGetDepthLabel(dm, &label));
4134 
4135   {
4136     /* Initialize roots and count leaves */
4137     PetscInt sMin = PETSC_MAX_INT;
4138     PetscInt sMax = PETSC_MIN_INT;
4139     PetscInt coneSize, supportSize;
4140 
4141     for (p = pStart; p < pEnd; ++p) {
4142       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4143       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4144       if (!coneSize && supportSize) {
4145         sMin = PetscMin(p, sMin);
4146         sMax = PetscMax(p, sMax);
4147         ++numRoots;
4148       } else if (!supportSize && coneSize) {
4149         ++numLeaves;
4150       } else if (!supportSize && !coneSize) {
4151         /* Isolated points */
4152         sMin = PetscMin(p, sMin);
4153         sMax = PetscMax(p, sMax);
4154       }
4155     }
4156     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4157   }
4158 
4159   if (numRoots + numLeaves == (pEnd - pStart)) {
4160     PetscInt sMin = PETSC_MAX_INT;
4161     PetscInt sMax = PETSC_MIN_INT;
4162     PetscInt coneSize, supportSize;
4163 
4164     for (p = pStart; p < pEnd; ++p) {
4165       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4166       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4167       if (!supportSize && coneSize) {
4168         sMin = PetscMin(p, sMin);
4169         sMax = PetscMax(p, sMax);
4170       }
4171     }
4172     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4173   } else {
4174     PetscInt level = 0;
4175     PetscInt qStart, qEnd, q;
4176 
4177     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4178     while (qEnd > qStart) {
4179       PetscInt sMin = PETSC_MAX_INT;
4180       PetscInt sMax = PETSC_MIN_INT;
4181 
4182       for (q = qStart; q < qEnd; ++q) {
4183         const PetscInt *support;
4184         PetscInt        supportSize, s;
4185 
4186         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4187         PetscCall(DMPlexGetSupport(dm, q, &support));
4188         for (s = 0; s < supportSize; ++s) {
4189           sMin = PetscMin(support[s], sMin);
4190           sMax = PetscMax(support[s], sMax);
4191         }
4192       }
4193       PetscCall(DMLabelGetNumValues(label, &level));
4194       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4195       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4196     }
4197   }
4198   { /* just in case there is an empty process */
4199     PetscInt numValues, maxValues = 0, v;
4200 
4201     PetscCall(DMLabelGetNumValues(label, &numValues));
4202     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4203     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4204   }
4205   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4206   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4207   PetscFunctionReturn(PETSC_SUCCESS);
4208 }
4209 
4210 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4211 {
4212   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4213   PetscInt       dim, depth, pheight, coneSize;
4214 
4215   PetscFunctionBeginHot;
4216   PetscCall(DMGetDimension(dm, &dim));
4217   PetscCall(DMPlexGetDepth(dm, &depth));
4218   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4219   pheight = depth - pdepth;
4220   if (depth <= 1) {
4221     switch (pdepth) {
4222     case 0:
4223       ct = DM_POLYTOPE_POINT;
4224       break;
4225     case 1:
4226       switch (coneSize) {
4227       case 2:
4228         ct = DM_POLYTOPE_SEGMENT;
4229         break;
4230       case 3:
4231         ct = DM_POLYTOPE_TRIANGLE;
4232         break;
4233       case 4:
4234         switch (dim) {
4235         case 2:
4236           ct = DM_POLYTOPE_QUADRILATERAL;
4237           break;
4238         case 3:
4239           ct = DM_POLYTOPE_TETRAHEDRON;
4240           break;
4241         default:
4242           break;
4243         }
4244         break;
4245       case 5:
4246         ct = DM_POLYTOPE_PYRAMID;
4247         break;
4248       case 6:
4249         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4250         break;
4251       case 8:
4252         ct = DM_POLYTOPE_HEXAHEDRON;
4253         break;
4254       default:
4255         break;
4256       }
4257     }
4258   } else {
4259     if (pdepth == 0) {
4260       ct = DM_POLYTOPE_POINT;
4261     } else if (pheight == 0) {
4262       switch (dim) {
4263       case 1:
4264         switch (coneSize) {
4265         case 2:
4266           ct = DM_POLYTOPE_SEGMENT;
4267           break;
4268         default:
4269           break;
4270         }
4271         break;
4272       case 2:
4273         switch (coneSize) {
4274         case 3:
4275           ct = DM_POLYTOPE_TRIANGLE;
4276           break;
4277         case 4:
4278           ct = DM_POLYTOPE_QUADRILATERAL;
4279           break;
4280         default:
4281           break;
4282         }
4283         break;
4284       case 3:
4285         switch (coneSize) {
4286         case 4:
4287           ct = DM_POLYTOPE_TETRAHEDRON;
4288           break;
4289         case 5: {
4290           const PetscInt *cone;
4291           PetscInt        faceConeSize;
4292 
4293           PetscCall(DMPlexGetCone(dm, p, &cone));
4294           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4295           switch (faceConeSize) {
4296           case 3:
4297             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4298             break;
4299           case 4:
4300             ct = DM_POLYTOPE_PYRAMID;
4301             break;
4302           }
4303         } break;
4304         case 6:
4305           ct = DM_POLYTOPE_HEXAHEDRON;
4306           break;
4307         default:
4308           break;
4309         }
4310         break;
4311       default:
4312         break;
4313       }
4314     } else if (pheight > 0) {
4315       switch (coneSize) {
4316       case 2:
4317         ct = DM_POLYTOPE_SEGMENT;
4318         break;
4319       case 3:
4320         ct = DM_POLYTOPE_TRIANGLE;
4321         break;
4322       case 4:
4323         ct = DM_POLYTOPE_QUADRILATERAL;
4324         break;
4325       default:
4326         break;
4327       }
4328     }
4329   }
4330   *pt = ct;
4331   PetscFunctionReturn(PETSC_SUCCESS);
4332 }
4333 
4334 /*@
4335   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4336 
4337   Collective
4338 
4339   Input Parameter:
4340 . dm - The `DMPLEX`
4341 
4342   Level: developer
4343 
4344   Note:
4345   This function is normally called automatically when a cell type is requested. It creates an
4346   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4347   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4348 
4349   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4350 
4351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4352 @*/
4353 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4354 {
4355   DM_Plex *mesh;
4356   DMLabel  ctLabel;
4357   PetscInt pStart, pEnd, p;
4358 
4359   PetscFunctionBegin;
4360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4361   mesh = (DM_Plex *)dm->data;
4362   PetscCall(DMCreateLabel(dm, "celltype"));
4363   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4364   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4365   PetscCall(PetscFree(mesh->cellTypes));
4366   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4367   for (p = pStart; p < pEnd; ++p) {
4368     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4369     PetscInt       pdepth;
4370 
4371     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4372     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4373     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4374     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4375     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4376   }
4377   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4378   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4379   PetscFunctionReturn(PETSC_SUCCESS);
4380 }
4381 
4382 /*@C
4383   DMPlexGetJoin - Get an array for the join of the set of points
4384 
4385   Not Collective
4386 
4387   Input Parameters:
4388 + dm        - The `DMPLEX` object
4389 . numPoints - The number of input points for the join
4390 - points    - The input points
4391 
4392   Output Parameters:
4393 + numCoveredPoints - The number of points in the join
4394 - coveredPoints    - The points in the join
4395 
4396   Level: intermediate
4397 
4398   Note:
4399   Currently, this is restricted to a single level join
4400 
4401   Fortran Notes:
4402   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4403 
4404 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4405 @*/
4406 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4407 {
4408   DM_Plex  *mesh = (DM_Plex *)dm->data;
4409   PetscInt *join[2];
4410   PetscInt  joinSize, i = 0;
4411   PetscInt  dof, off, p, c, m;
4412   PetscInt  maxSupportSize;
4413 
4414   PetscFunctionBegin;
4415   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4416   PetscAssertPointer(points, 3);
4417   PetscAssertPointer(numCoveredPoints, 4);
4418   PetscAssertPointer(coveredPoints, 5);
4419   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4420   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4421   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4422   /* Copy in support of first point */
4423   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4424   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4425   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4426   /* Check each successive support */
4427   for (p = 1; p < numPoints; ++p) {
4428     PetscInt newJoinSize = 0;
4429 
4430     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4431     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4432     for (c = 0; c < dof; ++c) {
4433       const PetscInt point = mesh->supports[off + c];
4434 
4435       for (m = 0; m < joinSize; ++m) {
4436         if (point == join[i][m]) {
4437           join[1 - i][newJoinSize++] = point;
4438           break;
4439         }
4440       }
4441     }
4442     joinSize = newJoinSize;
4443     i        = 1 - i;
4444   }
4445   *numCoveredPoints = joinSize;
4446   *coveredPoints    = join[i];
4447   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4448   PetscFunctionReturn(PETSC_SUCCESS);
4449 }
4450 
4451 /*@C
4452   DMPlexRestoreJoin - Restore an array for the join of the set of points
4453 
4454   Not Collective
4455 
4456   Input Parameters:
4457 + dm        - The `DMPLEX` object
4458 . numPoints - The number of input points for the join
4459 - points    - The input points
4460 
4461   Output Parameters:
4462 + numCoveredPoints - The number of points in the join
4463 - coveredPoints    - The points in the join
4464 
4465   Level: intermediate
4466 
4467   Fortran Notes:
4468   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4469 
4470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4471 @*/
4472 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4473 {
4474   PetscFunctionBegin;
4475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4476   if (points) PetscAssertPointer(points, 3);
4477   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4478   PetscAssertPointer(coveredPoints, 5);
4479   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4480   if (numCoveredPoints) *numCoveredPoints = 0;
4481   PetscFunctionReturn(PETSC_SUCCESS);
4482 }
4483 
4484 /*@C
4485   DMPlexGetFullJoin - Get an array for the join of the set of points
4486 
4487   Not Collective
4488 
4489   Input Parameters:
4490 + dm        - The `DMPLEX` object
4491 . numPoints - The number of input points for the join
4492 - points    - The input points
4493 
4494   Output Parameters:
4495 + numCoveredPoints - The number of points in the join
4496 - coveredPoints    - The points in the join
4497 
4498   Level: intermediate
4499 
4500   Fortran Notes:
4501   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4502 
4503 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4504 @*/
4505 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4506 {
4507   PetscInt *offsets, **closures;
4508   PetscInt *join[2];
4509   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4510   PetscInt  p, d, c, m, ms;
4511 
4512   PetscFunctionBegin;
4513   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4514   PetscAssertPointer(points, 3);
4515   PetscAssertPointer(numCoveredPoints, 4);
4516   PetscAssertPointer(coveredPoints, 5);
4517 
4518   PetscCall(DMPlexGetDepth(dm, &depth));
4519   PetscCall(PetscCalloc1(numPoints, &closures));
4520   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4521   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4522   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4523   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4524   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4525 
4526   for (p = 0; p < numPoints; ++p) {
4527     PetscInt closureSize;
4528 
4529     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4530 
4531     offsets[p * (depth + 2) + 0] = 0;
4532     for (d = 0; d < depth + 1; ++d) {
4533       PetscInt pStart, pEnd, i;
4534 
4535       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4536       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4537         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4538           offsets[p * (depth + 2) + d + 1] = i;
4539           break;
4540         }
4541       }
4542       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4543     }
4544     PetscCheck(offsets[p * (depth + 2) + depth + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (depth + 2) + depth + 1], closureSize);
4545   }
4546   for (d = 0; d < depth + 1; ++d) {
4547     PetscInt dof;
4548 
4549     /* Copy in support of first point */
4550     dof = offsets[d + 1] - offsets[d];
4551     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4552     /* Check each successive cone */
4553     for (p = 1; p < numPoints && joinSize; ++p) {
4554       PetscInt newJoinSize = 0;
4555 
4556       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4557       for (c = 0; c < dof; ++c) {
4558         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4559 
4560         for (m = 0; m < joinSize; ++m) {
4561           if (point == join[i][m]) {
4562             join[1 - i][newJoinSize++] = point;
4563             break;
4564           }
4565         }
4566       }
4567       joinSize = newJoinSize;
4568       i        = 1 - i;
4569     }
4570     if (joinSize) break;
4571   }
4572   *numCoveredPoints = joinSize;
4573   *coveredPoints    = join[i];
4574   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4575   PetscCall(PetscFree(closures));
4576   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4577   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4578   PetscFunctionReturn(PETSC_SUCCESS);
4579 }
4580 
4581 /*@C
4582   DMPlexGetMeet - Get an array for the meet of the set of points
4583 
4584   Not Collective
4585 
4586   Input Parameters:
4587 + dm        - The `DMPLEX` object
4588 . numPoints - The number of input points for the meet
4589 - points    - The input points
4590 
4591   Output Parameters:
4592 + numCoveringPoints - The number of points in the meet
4593 - coveringPoints    - The points in the meet
4594 
4595   Level: intermediate
4596 
4597   Note:
4598   Currently, this is restricted to a single level meet
4599 
4600   Fortran Notes:
4601   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4602 
4603 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4604 @*/
4605 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4606 {
4607   DM_Plex  *mesh = (DM_Plex *)dm->data;
4608   PetscInt *meet[2];
4609   PetscInt  meetSize, i = 0;
4610   PetscInt  dof, off, p, c, m;
4611   PetscInt  maxConeSize;
4612 
4613   PetscFunctionBegin;
4614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4615   PetscAssertPointer(points, 3);
4616   PetscAssertPointer(numCoveringPoints, 4);
4617   PetscAssertPointer(coveringPoints, 5);
4618   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4619   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4620   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4621   /* Copy in cone of first point */
4622   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4623   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4624   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4625   /* Check each successive cone */
4626   for (p = 1; p < numPoints; ++p) {
4627     PetscInt newMeetSize = 0;
4628 
4629     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4630     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4631     for (c = 0; c < dof; ++c) {
4632       const PetscInt point = mesh->cones[off + c];
4633 
4634       for (m = 0; m < meetSize; ++m) {
4635         if (point == meet[i][m]) {
4636           meet[1 - i][newMeetSize++] = point;
4637           break;
4638         }
4639       }
4640     }
4641     meetSize = newMeetSize;
4642     i        = 1 - i;
4643   }
4644   *numCoveringPoints = meetSize;
4645   *coveringPoints    = meet[i];
4646   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4647   PetscFunctionReturn(PETSC_SUCCESS);
4648 }
4649 
4650 /*@C
4651   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4652 
4653   Not Collective
4654 
4655   Input Parameters:
4656 + dm        - The `DMPLEX` object
4657 . numPoints - The number of input points for the meet
4658 - points    - The input points
4659 
4660   Output Parameters:
4661 + numCoveredPoints - The number of points in the meet
4662 - coveredPoints    - The points in the meet
4663 
4664   Level: intermediate
4665 
4666   Fortran Notes:
4667   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4668 
4669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4670 @*/
4671 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4672 {
4673   PetscFunctionBegin;
4674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4675   if (points) PetscAssertPointer(points, 3);
4676   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4677   PetscAssertPointer(coveredPoints, 5);
4678   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4679   if (numCoveredPoints) *numCoveredPoints = 0;
4680   PetscFunctionReturn(PETSC_SUCCESS);
4681 }
4682 
4683 /*@C
4684   DMPlexGetFullMeet - Get an array for the meet of the set of points
4685 
4686   Not Collective
4687 
4688   Input Parameters:
4689 + dm        - The `DMPLEX` object
4690 . numPoints - The number of input points for the meet
4691 - points    - The input points
4692 
4693   Output Parameters:
4694 + numCoveredPoints - The number of points in the meet
4695 - coveredPoints    - The points in the meet
4696 
4697   Level: intermediate
4698 
4699   Fortran Notes:
4700   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4701 
4702 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4703 @*/
4704 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4705 {
4706   PetscInt *offsets, **closures;
4707   PetscInt *meet[2];
4708   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4709   PetscInt  p, h, c, m, mc;
4710 
4711   PetscFunctionBegin;
4712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4713   PetscAssertPointer(points, 3);
4714   PetscAssertPointer(numCoveredPoints, 4);
4715   PetscAssertPointer(coveredPoints, 5);
4716 
4717   PetscCall(DMPlexGetDepth(dm, &height));
4718   PetscCall(PetscMalloc1(numPoints, &closures));
4719   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4720   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4721   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4722   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4723   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4724 
4725   for (p = 0; p < numPoints; ++p) {
4726     PetscInt closureSize;
4727 
4728     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4729 
4730     offsets[p * (height + 2) + 0] = 0;
4731     for (h = 0; h < height + 1; ++h) {
4732       PetscInt pStart, pEnd, i;
4733 
4734       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4735       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4736         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4737           offsets[p * (height + 2) + h + 1] = i;
4738           break;
4739         }
4740       }
4741       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4742     }
4743     PetscCheck(offsets[p * (height + 2) + height + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (height + 2) + height + 1], closureSize);
4744   }
4745   for (h = 0; h < height + 1; ++h) {
4746     PetscInt dof;
4747 
4748     /* Copy in cone of first point */
4749     dof = offsets[h + 1] - offsets[h];
4750     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4751     /* Check each successive cone */
4752     for (p = 1; p < numPoints && meetSize; ++p) {
4753       PetscInt newMeetSize = 0;
4754 
4755       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4756       for (c = 0; c < dof; ++c) {
4757         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4758 
4759         for (m = 0; m < meetSize; ++m) {
4760           if (point == meet[i][m]) {
4761             meet[1 - i][newMeetSize++] = point;
4762             break;
4763           }
4764         }
4765       }
4766       meetSize = newMeetSize;
4767       i        = 1 - i;
4768     }
4769     if (meetSize) break;
4770   }
4771   *numCoveredPoints = meetSize;
4772   *coveredPoints    = meet[i];
4773   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4774   PetscCall(PetscFree(closures));
4775   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4776   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4777   PetscFunctionReturn(PETSC_SUCCESS);
4778 }
4779 
4780 /*@C
4781   DMPlexEqual - Determine if two `DM` have the same topology
4782 
4783   Not Collective
4784 
4785   Input Parameters:
4786 + dmA - A `DMPLEX` object
4787 - dmB - A `DMPLEX` object
4788 
4789   Output Parameter:
4790 . equal - `PETSC_TRUE` if the topologies are identical
4791 
4792   Level: intermediate
4793 
4794   Note:
4795   We are not solving graph isomorphism, so we do not permute.
4796 
4797 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4798 @*/
4799 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4800 {
4801   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4802 
4803   PetscFunctionBegin;
4804   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4805   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4806   PetscAssertPointer(equal, 3);
4807 
4808   *equal = PETSC_FALSE;
4809   PetscCall(DMPlexGetDepth(dmA, &depth));
4810   PetscCall(DMPlexGetDepth(dmB, &depthB));
4811   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4812   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4813   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4814   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4815   for (p = pStart; p < pEnd; ++p) {
4816     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4817     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4818 
4819     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4820     PetscCall(DMPlexGetCone(dmA, p, &cone));
4821     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4822     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4823     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4824     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4825     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4826     for (c = 0; c < coneSize; ++c) {
4827       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4828       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4829     }
4830     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4831     PetscCall(DMPlexGetSupport(dmA, p, &support));
4832     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4833     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4834     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4835     for (s = 0; s < supportSize; ++s) {
4836       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4837     }
4838   }
4839   *equal = PETSC_TRUE;
4840   PetscFunctionReturn(PETSC_SUCCESS);
4841 }
4842 
4843 /*@C
4844   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4845 
4846   Not Collective
4847 
4848   Input Parameters:
4849 + dm         - The `DMPLEX`
4850 . cellDim    - The cell dimension
4851 - numCorners - The number of vertices on a cell
4852 
4853   Output Parameter:
4854 . numFaceVertices - The number of vertices on a face
4855 
4856   Level: developer
4857 
4858   Note:
4859   Of course this can only work for a restricted set of symmetric shapes
4860 
4861 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4862 @*/
4863 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4864 {
4865   MPI_Comm comm;
4866 
4867   PetscFunctionBegin;
4868   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4869   PetscAssertPointer(numFaceVertices, 4);
4870   switch (cellDim) {
4871   case 0:
4872     *numFaceVertices = 0;
4873     break;
4874   case 1:
4875     *numFaceVertices = 1;
4876     break;
4877   case 2:
4878     switch (numCorners) {
4879     case 3:                 /* triangle */
4880       *numFaceVertices = 2; /* Edge has 2 vertices */
4881       break;
4882     case 4:                 /* quadrilateral */
4883       *numFaceVertices = 2; /* Edge has 2 vertices */
4884       break;
4885     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4886       *numFaceVertices = 3; /* Edge has 3 vertices */
4887       break;
4888     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4889       *numFaceVertices = 3; /* Edge has 3 vertices */
4890       break;
4891     default:
4892       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4893     }
4894     break;
4895   case 3:
4896     switch (numCorners) {
4897     case 4:                 /* tetradehdron */
4898       *numFaceVertices = 3; /* Face has 3 vertices */
4899       break;
4900     case 6:                 /* tet cohesive cells */
4901       *numFaceVertices = 4; /* Face has 4 vertices */
4902       break;
4903     case 8:                 /* hexahedron */
4904       *numFaceVertices = 4; /* Face has 4 vertices */
4905       break;
4906     case 9:                 /* tet cohesive Lagrange cells */
4907       *numFaceVertices = 6; /* Face has 6 vertices */
4908       break;
4909     case 10:                /* quadratic tetrahedron */
4910       *numFaceVertices = 6; /* Face has 6 vertices */
4911       break;
4912     case 12:                /* hex cohesive Lagrange cells */
4913       *numFaceVertices = 6; /* Face has 6 vertices */
4914       break;
4915     case 18:                /* quadratic tet cohesive Lagrange cells */
4916       *numFaceVertices = 6; /* Face has 6 vertices */
4917       break;
4918     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4919       *numFaceVertices = 9; /* Face has 9 vertices */
4920       break;
4921     default:
4922       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4923     }
4924     break;
4925   default:
4926     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4927   }
4928   PetscFunctionReturn(PETSC_SUCCESS);
4929 }
4930 
4931 /*@
4932   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4933 
4934   Not Collective
4935 
4936   Input Parameter:
4937 . dm - The `DMPLEX` object
4938 
4939   Output Parameter:
4940 . depthLabel - The `DMLabel` recording point depth
4941 
4942   Level: developer
4943 
4944 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4945 @*/
4946 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4947 {
4948   PetscFunctionBegin;
4949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4950   PetscAssertPointer(depthLabel, 2);
4951   *depthLabel = dm->depthLabel;
4952   PetscFunctionReturn(PETSC_SUCCESS);
4953 }
4954 
4955 /*@
4956   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4957 
4958   Not Collective
4959 
4960   Input Parameter:
4961 . dm - The `DMPLEX` object
4962 
4963   Output Parameter:
4964 . depth - The number of strata (breadth first levels) in the DAG
4965 
4966   Level: developer
4967 
4968   Notes:
4969   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4970 
4971   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4972 
4973   An empty mesh gives -1.
4974 
4975 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4976 @*/
4977 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4978 {
4979   DM_Plex *mesh = (DM_Plex *)dm->data;
4980   DMLabel  label;
4981   PetscInt d = 0;
4982 
4983   PetscFunctionBegin;
4984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4985   PetscAssertPointer(depth, 2);
4986   if (mesh->tr) {
4987     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4988   } else {
4989     PetscCall(DMPlexGetDepthLabel(dm, &label));
4990     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4991     *depth = d - 1;
4992   }
4993   PetscFunctionReturn(PETSC_SUCCESS);
4994 }
4995 
4996 /*@
4997   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
4998 
4999   Not Collective
5000 
5001   Input Parameters:
5002 + dm    - The `DMPLEX` object
5003 - depth - The requested depth
5004 
5005   Output Parameters:
5006 + start - The first point at this `depth`
5007 - end   - One beyond the last point at this `depth`
5008 
5009   Level: developer
5010 
5011   Notes:
5012   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5013   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5014   higher dimension, e.g., "edges".
5015 
5016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5017 @*/
5018 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5019 {
5020   DM_Plex *mesh = (DM_Plex *)dm->data;
5021   DMLabel  label;
5022   PetscInt pStart, pEnd;
5023 
5024   PetscFunctionBegin;
5025   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5026   if (start) {
5027     PetscAssertPointer(start, 3);
5028     *start = 0;
5029   }
5030   if (end) {
5031     PetscAssertPointer(end, 4);
5032     *end = 0;
5033   }
5034   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5035   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5036   if (depth < 0) {
5037     if (start) *start = pStart;
5038     if (end) *end = pEnd;
5039     PetscFunctionReturn(PETSC_SUCCESS);
5040   }
5041   if (mesh->tr) {
5042     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5043   } else {
5044     PetscCall(DMPlexGetDepthLabel(dm, &label));
5045     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5046     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5047   }
5048   PetscFunctionReturn(PETSC_SUCCESS);
5049 }
5050 
5051 /*@
5052   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5053 
5054   Not Collective
5055 
5056   Input Parameters:
5057 + dm     - The `DMPLEX` object
5058 - height - The requested height
5059 
5060   Output Parameters:
5061 + start - The first point at this `height`
5062 - end   - One beyond the last point at this `height`
5063 
5064   Level: developer
5065 
5066   Notes:
5067   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5068   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5069   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5070 
5071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5072 @*/
5073 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5074 {
5075   DMLabel  label;
5076   PetscInt depth, pStart, pEnd;
5077 
5078   PetscFunctionBegin;
5079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5080   if (start) {
5081     PetscAssertPointer(start, 3);
5082     *start = 0;
5083   }
5084   if (end) {
5085     PetscAssertPointer(end, 4);
5086     *end = 0;
5087   }
5088   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5089   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5090   if (height < 0) {
5091     if (start) *start = pStart;
5092     if (end) *end = pEnd;
5093     PetscFunctionReturn(PETSC_SUCCESS);
5094   }
5095   PetscCall(DMPlexGetDepthLabel(dm, &label));
5096   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5097   else PetscCall(DMGetDimension(dm, &depth));
5098   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5099   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5100   PetscFunctionReturn(PETSC_SUCCESS);
5101 }
5102 
5103 /*@
5104   DMPlexGetPointDepth - Get the `depth` of a given point
5105 
5106   Not Collective
5107 
5108   Input Parameters:
5109 + dm    - The `DMPLEX` object
5110 - point - The point
5111 
5112   Output Parameter:
5113 . depth - The depth of the `point`
5114 
5115   Level: intermediate
5116 
5117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5118 @*/
5119 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5120 {
5121   PetscFunctionBegin;
5122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5123   PetscAssertPointer(depth, 3);
5124   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5125   PetscFunctionReturn(PETSC_SUCCESS);
5126 }
5127 
5128 /*@
5129   DMPlexGetPointHeight - Get the `height` of a given point
5130 
5131   Not Collective
5132 
5133   Input Parameters:
5134 + dm    - The `DMPLEX` object
5135 - point - The point
5136 
5137   Output Parameter:
5138 . height - The height of the `point`
5139 
5140   Level: intermediate
5141 
5142 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5143 @*/
5144 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5145 {
5146   PetscInt n, pDepth;
5147 
5148   PetscFunctionBegin;
5149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5150   PetscAssertPointer(height, 3);
5151   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5152   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5153   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5154   PetscFunctionReturn(PETSC_SUCCESS);
5155 }
5156 
5157 /*@
5158   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5159 
5160   Not Collective
5161 
5162   Input Parameter:
5163 . dm - The `DMPLEX` object
5164 
5165   Output Parameter:
5166 . celltypeLabel - The `DMLabel` recording cell polytope type
5167 
5168   Level: developer
5169 
5170   Note:
5171   This function will trigger automatica computation of cell types. This can be disabled by calling
5172   `DMCreateLabel`(dm, "celltype") beforehand.
5173 
5174 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5175 @*/
5176 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5177 {
5178   PetscFunctionBegin;
5179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5180   PetscAssertPointer(celltypeLabel, 2);
5181   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5182   *celltypeLabel = dm->celltypeLabel;
5183   PetscFunctionReturn(PETSC_SUCCESS);
5184 }
5185 
5186 /*@
5187   DMPlexGetCellType - Get 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 
5195   Output Parameter:
5196 . celltype - The polytope type of the cell
5197 
5198   Level: intermediate
5199 
5200 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5201 @*/
5202 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5203 {
5204   DM_Plex *mesh = (DM_Plex *)dm->data;
5205   DMLabel  label;
5206   PetscInt ct;
5207 
5208   PetscFunctionBegin;
5209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5210   PetscAssertPointer(celltype, 3);
5211   if (mesh->tr) {
5212     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5213   } else {
5214     PetscInt pStart, pEnd;
5215 
5216     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5217     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5218       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5219       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5220       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5221       for (PetscInt p = pStart; p < pEnd; p++) {
5222         PetscCall(DMLabelGetValue(label, p, &ct));
5223         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5224       }
5225     }
5226     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5227     if (PetscDefined(USE_DEBUG)) {
5228       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5229       PetscCall(DMLabelGetValue(label, cell, &ct));
5230       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5231       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5232     }
5233   }
5234   PetscFunctionReturn(PETSC_SUCCESS);
5235 }
5236 
5237 /*@
5238   DMPlexSetCellType - Set the polytope type of a given cell
5239 
5240   Not Collective
5241 
5242   Input Parameters:
5243 + dm       - The `DMPLEX` object
5244 . cell     - The cell
5245 - celltype - The polytope type of the cell
5246 
5247   Level: advanced
5248 
5249   Note:
5250   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5251   is executed. This function will override the computed type. However, if automatic classification will not succeed
5252   and a user wants to manually specify all types, the classification must be disabled by calling
5253   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5254 
5255 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5256 @*/
5257 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5258 {
5259   DM_Plex *mesh = (DM_Plex *)dm->data;
5260   DMLabel  label;
5261   PetscInt pStart, pEnd;
5262 
5263   PetscFunctionBegin;
5264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5265   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5266   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5267   PetscCall(DMLabelSetValue(label, cell, celltype));
5268   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5269   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5270   PetscFunctionReturn(PETSC_SUCCESS);
5271 }
5272 
5273 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5274 {
5275   PetscSection section, s;
5276   Mat          m;
5277   PetscInt     maxHeight;
5278   const char  *prefix;
5279 
5280   PetscFunctionBegin;
5281   PetscCall(DMClone(dm, cdm));
5282   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5283   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5284   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5285   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5286   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5287   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5288   PetscCall(DMSetLocalSection(*cdm, section));
5289   PetscCall(PetscSectionDestroy(&section));
5290   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5291   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5292   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5293   PetscCall(PetscSectionDestroy(&s));
5294   PetscCall(MatDestroy(&m));
5295 
5296   PetscCall(DMSetNumFields(*cdm, 1));
5297   PetscCall(DMCreateDS(*cdm));
5298   (*cdm)->cloneOpts = PETSC_TRUE;
5299   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5300   PetscFunctionReturn(PETSC_SUCCESS);
5301 }
5302 
5303 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5304 {
5305   Vec coordsLocal, cellCoordsLocal;
5306   DM  coordsDM, cellCoordsDM;
5307 
5308   PetscFunctionBegin;
5309   *field = NULL;
5310   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5311   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5312   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5313   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5314   if (coordsLocal && coordsDM) {
5315     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5316     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5317   }
5318   PetscFunctionReturn(PETSC_SUCCESS);
5319 }
5320 
5321 /*@C
5322   DMPlexGetConeSection - Return a section which describes the layout of cone data
5323 
5324   Not Collective
5325 
5326   Input Parameter:
5327 . dm - The `DMPLEX` object
5328 
5329   Output Parameter:
5330 . section - The `PetscSection` object
5331 
5332   Level: developer
5333 
5334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5335 @*/
5336 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5337 {
5338   DM_Plex *mesh = (DM_Plex *)dm->data;
5339 
5340   PetscFunctionBegin;
5341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5342   if (section) *section = mesh->coneSection;
5343   PetscFunctionReturn(PETSC_SUCCESS);
5344 }
5345 
5346 /*@C
5347   DMPlexGetSupportSection - Return a section which describes the layout of support data
5348 
5349   Not Collective
5350 
5351   Input Parameter:
5352 . dm - The `DMPLEX` object
5353 
5354   Output Parameter:
5355 . section - The `PetscSection` object
5356 
5357   Level: developer
5358 
5359 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5360 @*/
5361 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5362 {
5363   DM_Plex *mesh = (DM_Plex *)dm->data;
5364 
5365   PetscFunctionBegin;
5366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5367   if (section) *section = mesh->supportSection;
5368   PetscFunctionReturn(PETSC_SUCCESS);
5369 }
5370 
5371 /*@C
5372   DMPlexGetCones - Return cone data
5373 
5374   Not Collective
5375 
5376   Input Parameter:
5377 . dm - The `DMPLEX` object
5378 
5379   Output Parameter:
5380 . cones - The cone for each point
5381 
5382   Level: developer
5383 
5384 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5385 @*/
5386 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5387 {
5388   DM_Plex *mesh = (DM_Plex *)dm->data;
5389 
5390   PetscFunctionBegin;
5391   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5392   if (cones) *cones = mesh->cones;
5393   PetscFunctionReturn(PETSC_SUCCESS);
5394 }
5395 
5396 /*@C
5397   DMPlexGetConeOrientations - Return cone orientation data
5398 
5399   Not Collective
5400 
5401   Input Parameter:
5402 . dm - The `DMPLEX` object
5403 
5404   Output Parameter:
5405 . coneOrientations - The array of cone orientations for all points
5406 
5407   Level: developer
5408 
5409   Notes:
5410   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5411 
5412   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5413 
5414 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5415 @*/
5416 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5417 {
5418   DM_Plex *mesh = (DM_Plex *)dm->data;
5419 
5420   PetscFunctionBegin;
5421   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5422   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5423   PetscFunctionReturn(PETSC_SUCCESS);
5424 }
5425 
5426 /******************************** FEM Support **********************************/
5427 
5428 /*
5429  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5430  representing a line in the section.
5431 */
5432 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5433 {
5434   PetscFunctionBeginHot;
5435   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5436   if (line < 0) {
5437     *k  = 0;
5438     *Nc = 0;
5439   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5440     *k = 1;
5441   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5442     /* An order k SEM disc has k-1 dofs on an edge */
5443     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5444     *k = *k / *Nc + 1;
5445   }
5446   PetscFunctionReturn(PETSC_SUCCESS);
5447 }
5448 
5449 /*@
5450 
5451   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5452   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5453   section provided (or the section of the `DM`).
5454 
5455   Input Parameters:
5456 + dm      - The `DM`
5457 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5458 - section - The `PetscSection` to reorder, or `NULL` for the default section
5459 
5460   Example:
5461   A typical interpolated single-quad mesh might order points as
5462 .vb
5463   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5464 
5465   v4 -- e6 -- v3
5466   |           |
5467   e7    c0    e8
5468   |           |
5469   v1 -- e5 -- v2
5470 .ve
5471 
5472   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5473   dofs in the order of points, e.g.,
5474 .vb
5475     c0 -> [0,1,2,3]
5476     v1 -> [4]
5477     ...
5478     e5 -> [8, 9]
5479 .ve
5480 
5481   which corresponds to the dofs
5482 .vb
5483     6   10  11  7
5484     13  2   3   15
5485     12  0   1   14
5486     4   8   9   5
5487 .ve
5488 
5489   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5490 .vb
5491   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5492 .ve
5493 
5494   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5495 .vb
5496    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5497 .ve
5498 
5499   Level: developer
5500 
5501   Note:
5502   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5503   degree of the basis.
5504 
5505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5506 @*/
5507 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5508 {
5509   DMLabel   label;
5510   PetscInt  dim, depth = -1, eStart = -1, Nf;
5511   PetscBool vertexchart;
5512 
5513   PetscFunctionBegin;
5514   PetscCall(DMGetDimension(dm, &dim));
5515   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5516   if (point < 0) {
5517     PetscInt sStart, sEnd;
5518 
5519     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5520     point = sEnd - sStart ? sStart : point;
5521   }
5522   PetscCall(DMPlexGetDepthLabel(dm, &label));
5523   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5524   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5525   if (depth == 1) {
5526     eStart = point;
5527   } else if (depth == dim) {
5528     const PetscInt *cone;
5529 
5530     PetscCall(DMPlexGetCone(dm, point, &cone));
5531     if (dim == 2) eStart = cone[0];
5532     else if (dim == 3) {
5533       const PetscInt *cone2;
5534       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5535       eStart = cone2[0];
5536     } 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);
5537   } 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);
5538   { /* Determine whether the chart covers all points or just vertices. */
5539     PetscInt pStart, pEnd, cStart, cEnd;
5540     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5541     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5542     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5543     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5544     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5545   }
5546   PetscCall(PetscSectionGetNumFields(section, &Nf));
5547   for (PetscInt d = 1; d <= dim; d++) {
5548     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5549     PetscInt *perm;
5550 
5551     for (f = 0; f < Nf; ++f) {
5552       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5553       size += PetscPowInt(k + 1, d) * Nc;
5554     }
5555     PetscCall(PetscMalloc1(size, &perm));
5556     for (f = 0; f < Nf; ++f) {
5557       switch (d) {
5558       case 1:
5559         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5560         /*
5561          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5562          We want              [ vtx0; edge of length k-1; vtx1 ]
5563          */
5564         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5565         for (i = 0; i < k - 1; i++)
5566           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5567         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5568         foffset = offset;
5569         break;
5570       case 2:
5571         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5572         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5573         /* The SEM order is
5574 
5575          v_lb, {e_b}, v_rb,
5576          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5577          v_lt, reverse {e_t}, v_rt
5578          */
5579         {
5580           const PetscInt of   = 0;
5581           const PetscInt oeb  = of + PetscSqr(k - 1);
5582           const PetscInt oer  = oeb + (k - 1);
5583           const PetscInt oet  = oer + (k - 1);
5584           const PetscInt oel  = oet + (k - 1);
5585           const PetscInt ovlb = oel + (k - 1);
5586           const PetscInt ovrb = ovlb + 1;
5587           const PetscInt ovrt = ovrb + 1;
5588           const PetscInt ovlt = ovrt + 1;
5589           PetscInt       o;
5590 
5591           /* bottom */
5592           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5593           for (o = oeb; o < oer; ++o)
5594             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5595           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5596           /* middle */
5597           for (i = 0; i < k - 1; ++i) {
5598             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5599             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5600               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5601             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5602           }
5603           /* top */
5604           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5605           for (o = oel - 1; o >= oet; --o)
5606             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5607           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5608           foffset = offset;
5609         }
5610         break;
5611       case 3:
5612         /* The original hex closure is
5613 
5614          {c,
5615          f_b, f_t, f_f, f_b, f_r, f_l,
5616          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5617          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5618          */
5619         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5620         /* The SEM order is
5621          Bottom Slice
5622          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5623          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5624          v_blb, {e_bb}, v_brb,
5625 
5626          Middle Slice (j)
5627          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5628          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5629          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5630 
5631          Top Slice
5632          v_tlf, {e_tf}, v_trf,
5633          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5634          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5635          */
5636         {
5637           const PetscInt oc    = 0;
5638           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5639           const PetscInt oft   = ofb + PetscSqr(k - 1);
5640           const PetscInt off   = oft + PetscSqr(k - 1);
5641           const PetscInt ofk   = off + PetscSqr(k - 1);
5642           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5643           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5644           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5645           const PetscInt oebb  = oebl + (k - 1);
5646           const PetscInt oebr  = oebb + (k - 1);
5647           const PetscInt oebf  = oebr + (k - 1);
5648           const PetscInt oetf  = oebf + (k - 1);
5649           const PetscInt oetr  = oetf + (k - 1);
5650           const PetscInt oetb  = oetr + (k - 1);
5651           const PetscInt oetl  = oetb + (k - 1);
5652           const PetscInt oerf  = oetl + (k - 1);
5653           const PetscInt oelf  = oerf + (k - 1);
5654           const PetscInt oelb  = oelf + (k - 1);
5655           const PetscInt oerb  = oelb + (k - 1);
5656           const PetscInt ovblf = oerb + (k - 1);
5657           const PetscInt ovblb = ovblf + 1;
5658           const PetscInt ovbrb = ovblb + 1;
5659           const PetscInt ovbrf = ovbrb + 1;
5660           const PetscInt ovtlf = ovbrf + 1;
5661           const PetscInt ovtrf = ovtlf + 1;
5662           const PetscInt ovtrb = ovtrf + 1;
5663           const PetscInt ovtlb = ovtrb + 1;
5664           PetscInt       o, n;
5665 
5666           /* Bottom Slice */
5667           /*   bottom */
5668           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5669           for (o = oetf - 1; o >= oebf; --o)
5670             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5671           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5672           /*   middle */
5673           for (i = 0; i < k - 1; ++i) {
5674             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5675             for (n = 0; n < k - 1; ++n) {
5676               o = ofb + n * (k - 1) + i;
5677               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5678             }
5679             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5680           }
5681           /*   top */
5682           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5683           for (o = oebb; o < oebr; ++o)
5684             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5685           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5686 
5687           /* Middle Slice */
5688           for (j = 0; j < k - 1; ++j) {
5689             /*   bottom */
5690             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5691             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5692               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5693             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5694             /*   middle */
5695             for (i = 0; i < k - 1; ++i) {
5696               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5697               for (n = 0; n < k - 1; ++n)
5698                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5699               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5700             }
5701             /*   top */
5702             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5703             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5704               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5705             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5706           }
5707 
5708           /* Top Slice */
5709           /*   bottom */
5710           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5711           for (o = oetf; o < oetr; ++o)
5712             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5713           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5714           /*   middle */
5715           for (i = 0; i < k - 1; ++i) {
5716             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5717             for (n = 0; n < k - 1; ++n)
5718               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5719             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5720           }
5721           /*   top */
5722           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5723           for (o = oetl - 1; o >= oetb; --o)
5724             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5725           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5726 
5727           foffset = offset;
5728         }
5729         break;
5730       default:
5731         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5732       }
5733     }
5734     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5735     /* Check permutation */
5736     {
5737       PetscInt *check;
5738 
5739       PetscCall(PetscMalloc1(size, &check));
5740       for (i = 0; i < size; ++i) {
5741         check[i] = -1;
5742         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5743       }
5744       for (i = 0; i < size; ++i) check[perm[i]] = i;
5745       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5746       PetscCall(PetscFree(check));
5747     }
5748     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5749     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5750       PetscInt *loc_perm;
5751       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5752       for (PetscInt i = 0; i < size; i++) {
5753         loc_perm[i]        = perm[i];
5754         loc_perm[size + i] = size + perm[i];
5755       }
5756       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5757     }
5758   }
5759   PetscFunctionReturn(PETSC_SUCCESS);
5760 }
5761 
5762 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5763 {
5764   PetscDS  prob;
5765   PetscInt depth, Nf, h;
5766   DMLabel  label;
5767 
5768   PetscFunctionBeginHot;
5769   PetscCall(DMGetDS(dm, &prob));
5770   Nf      = prob->Nf;
5771   label   = dm->depthLabel;
5772   *dspace = NULL;
5773   if (field < Nf) {
5774     PetscObject disc = prob->disc[field];
5775 
5776     if (disc->classid == PETSCFE_CLASSID) {
5777       PetscDualSpace dsp;
5778 
5779       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5780       PetscCall(DMLabelGetNumValues(label, &depth));
5781       PetscCall(DMLabelGetValue(label, point, &h));
5782       h = depth - 1 - h;
5783       if (h) {
5784         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5785       } else {
5786         *dspace = dsp;
5787       }
5788     }
5789   }
5790   PetscFunctionReturn(PETSC_SUCCESS);
5791 }
5792 
5793 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5794 {
5795   PetscScalar       *array;
5796   const PetscScalar *vArray;
5797   const PetscInt    *cone, *coneO;
5798   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5799 
5800   PetscFunctionBeginHot;
5801   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5802   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5803   PetscCall(DMPlexGetCone(dm, point, &cone));
5804   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5805   if (!values || !*values) {
5806     if ((point >= pStart) && (point < pEnd)) {
5807       PetscInt dof;
5808 
5809       PetscCall(PetscSectionGetDof(section, point, &dof));
5810       size += dof;
5811     }
5812     for (p = 0; p < numPoints; ++p) {
5813       const PetscInt cp = cone[p];
5814       PetscInt       dof;
5815 
5816       if ((cp < pStart) || (cp >= pEnd)) continue;
5817       PetscCall(PetscSectionGetDof(section, cp, &dof));
5818       size += dof;
5819     }
5820     if (!values) {
5821       if (csize) *csize = size;
5822       PetscFunctionReturn(PETSC_SUCCESS);
5823     }
5824     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5825   } else {
5826     array = *values;
5827   }
5828   size = 0;
5829   PetscCall(VecGetArrayRead(v, &vArray));
5830   if ((point >= pStart) && (point < pEnd)) {
5831     PetscInt           dof, off, d;
5832     const PetscScalar *varr;
5833 
5834     PetscCall(PetscSectionGetDof(section, point, &dof));
5835     PetscCall(PetscSectionGetOffset(section, point, &off));
5836     varr = &vArray[off];
5837     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5838     size += dof;
5839   }
5840   for (p = 0; p < numPoints; ++p) {
5841     const PetscInt     cp = cone[p];
5842     PetscInt           o  = coneO[p];
5843     PetscInt           dof, off, d;
5844     const PetscScalar *varr;
5845 
5846     if ((cp < pStart) || (cp >= pEnd)) continue;
5847     PetscCall(PetscSectionGetDof(section, cp, &dof));
5848     PetscCall(PetscSectionGetOffset(section, cp, &off));
5849     varr = &vArray[off];
5850     if (o >= 0) {
5851       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5852     } else {
5853       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5854     }
5855     size += dof;
5856   }
5857   PetscCall(VecRestoreArrayRead(v, &vArray));
5858   if (!*values) {
5859     if (csize) *csize = size;
5860     *values = array;
5861   } else {
5862     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5863     *csize = size;
5864   }
5865   PetscFunctionReturn(PETSC_SUCCESS);
5866 }
5867 
5868 /* Compress out points not in the section */
5869 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5870 {
5871   const PetscInt np = *numPoints;
5872   PetscInt       pStart, pEnd, p, q;
5873 
5874   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5875   for (p = 0, q = 0; p < np; ++p) {
5876     const PetscInt r = points[p * 2];
5877     if ((r >= pStart) && (r < pEnd)) {
5878       points[q * 2]     = r;
5879       points[q * 2 + 1] = points[p * 2 + 1];
5880       ++q;
5881     }
5882   }
5883   *numPoints = q;
5884   return PETSC_SUCCESS;
5885 }
5886 
5887 /* Compressed closure does not apply closure permutation */
5888 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5889 {
5890   const PetscInt *cla = NULL;
5891   PetscInt        np, *pts = NULL;
5892 
5893   PetscFunctionBeginHot;
5894   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5895   if (!ornt && *clPoints) {
5896     PetscInt dof, off;
5897 
5898     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5899     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5900     PetscCall(ISGetIndices(*clPoints, &cla));
5901     np  = dof / 2;
5902     pts = (PetscInt *)&cla[off];
5903   } else {
5904     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5905     PetscCall(CompressPoints_Private(section, &np, pts));
5906   }
5907   *numPoints = np;
5908   *points    = pts;
5909   *clp       = cla;
5910   PetscFunctionReturn(PETSC_SUCCESS);
5911 }
5912 
5913 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5914 {
5915   PetscFunctionBeginHot;
5916   if (!*clPoints) {
5917     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5918   } else {
5919     PetscCall(ISRestoreIndices(*clPoints, clp));
5920   }
5921   *numPoints = 0;
5922   *points    = NULL;
5923   *clSec     = NULL;
5924   *clPoints  = NULL;
5925   *clp       = NULL;
5926   PetscFunctionReturn(PETSC_SUCCESS);
5927 }
5928 
5929 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5930 {
5931   PetscInt            offset = 0, p;
5932   const PetscInt    **perms  = NULL;
5933   const PetscScalar **flips  = NULL;
5934 
5935   PetscFunctionBeginHot;
5936   *size = 0;
5937   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5938   for (p = 0; p < numPoints; p++) {
5939     const PetscInt     point = points[2 * p];
5940     const PetscInt    *perm  = perms ? perms[p] : NULL;
5941     const PetscScalar *flip  = flips ? flips[p] : NULL;
5942     PetscInt           dof, off, d;
5943     const PetscScalar *varr;
5944 
5945     PetscCall(PetscSectionGetDof(section, point, &dof));
5946     PetscCall(PetscSectionGetOffset(section, point, &off));
5947     varr = &vArray[off];
5948     if (clperm) {
5949       if (perm) {
5950         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5951       } else {
5952         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5953       }
5954       if (flip) {
5955         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5956       }
5957     } else {
5958       if (perm) {
5959         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5960       } else {
5961         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5962       }
5963       if (flip) {
5964         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5965       }
5966     }
5967     offset += dof;
5968   }
5969   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5970   *size = offset;
5971   PetscFunctionReturn(PETSC_SUCCESS);
5972 }
5973 
5974 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[])
5975 {
5976   PetscInt offset = 0, f;
5977 
5978   PetscFunctionBeginHot;
5979   *size = 0;
5980   for (f = 0; f < numFields; ++f) {
5981     PetscInt            p;
5982     const PetscInt    **perms = NULL;
5983     const PetscScalar **flips = NULL;
5984 
5985     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5986     for (p = 0; p < numPoints; p++) {
5987       const PetscInt     point = points[2 * p];
5988       PetscInt           fdof, foff, b;
5989       const PetscScalar *varr;
5990       const PetscInt    *perm = perms ? perms[p] : NULL;
5991       const PetscScalar *flip = flips ? flips[p] : NULL;
5992 
5993       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5994       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5995       varr = &vArray[foff];
5996       if (clperm) {
5997         if (perm) {
5998           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5999         } else {
6000           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6001         }
6002         if (flip) {
6003           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6004         }
6005       } else {
6006         if (perm) {
6007           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6008         } else {
6009           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6010         }
6011         if (flip) {
6012           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6013         }
6014       }
6015       offset += fdof;
6016     }
6017     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6018   }
6019   *size = offset;
6020   PetscFunctionReturn(PETSC_SUCCESS);
6021 }
6022 
6023 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6024 {
6025   PetscSection    clSection;
6026   IS              clPoints;
6027   PetscInt       *points = NULL;
6028   const PetscInt *clp, *perm;
6029   PetscInt        depth, numFields, numPoints, asize;
6030 
6031   PetscFunctionBeginHot;
6032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6033   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6034   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6035   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6036   PetscCall(DMPlexGetDepth(dm, &depth));
6037   PetscCall(PetscSectionGetNumFields(section, &numFields));
6038   if (depth == 1 && numFields < 2) {
6039     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6040     PetscFunctionReturn(PETSC_SUCCESS);
6041   }
6042   /* Get points */
6043   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6044   /* Get sizes */
6045   asize = 0;
6046   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6047     PetscInt dof;
6048     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6049     asize += dof;
6050   }
6051   if (values) {
6052     const PetscScalar *vArray;
6053     PetscInt           size;
6054 
6055     if (*values) {
6056       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);
6057     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6058     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6059     PetscCall(VecGetArrayRead(v, &vArray));
6060     /* Get values */
6061     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6062     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6063     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6064     /* Cleanup array */
6065     PetscCall(VecRestoreArrayRead(v, &vArray));
6066   }
6067   if (csize) *csize = asize;
6068   /* Cleanup points */
6069   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6070   PetscFunctionReturn(PETSC_SUCCESS);
6071 }
6072 
6073 /*@C
6074   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6075 
6076   Not collective
6077 
6078   Input Parameters:
6079 + dm      - The `DM`
6080 . section - The section describing the layout in `v`, or `NULL` to use the default section
6081 . v       - The local vector
6082 - point   - The point in the `DM`
6083 
6084   Input/Output Parameters:
6085 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6086 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6087            if the user provided `NULL`, it is a borrowed array and should not be freed
6088 
6089   Level: intermediate
6090 
6091   Notes:
6092   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6093   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6094   assembly function, and a user may already have allocated storage for this operation.
6095 
6096   A typical use could be
6097 .vb
6098    values = NULL;
6099    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6100    for (cl = 0; cl < clSize; ++cl) {
6101      <Compute on closure>
6102    }
6103    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6104 .ve
6105   or
6106 .vb
6107    PetscMalloc1(clMaxSize, &values);
6108    for (p = pStart; p < pEnd; ++p) {
6109      clSize = clMaxSize;
6110      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6111      for (cl = 0; cl < clSize; ++cl) {
6112        <Compute on closure>
6113      }
6114    }
6115    PetscFree(values);
6116 .ve
6117 
6118   Fortran Notes:
6119   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6120 
6121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6122 @*/
6123 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6124 {
6125   PetscFunctionBeginHot;
6126   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6127   PetscFunctionReturn(PETSC_SUCCESS);
6128 }
6129 
6130 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6131 {
6132   DMLabel            depthLabel;
6133   PetscSection       clSection;
6134   IS                 clPoints;
6135   PetscScalar       *array;
6136   const PetscScalar *vArray;
6137   PetscInt          *points = NULL;
6138   const PetscInt    *clp, *perm = NULL;
6139   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6140 
6141   PetscFunctionBeginHot;
6142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6143   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6144   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6145   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6146   PetscCall(DMPlexGetDepth(dm, &mdepth));
6147   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6148   PetscCall(PetscSectionGetNumFields(section, &numFields));
6149   if (mdepth == 1 && numFields < 2) {
6150     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6151     PetscFunctionReturn(PETSC_SUCCESS);
6152   }
6153   /* Get points */
6154   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6155   for (clsize = 0, p = 0; p < Np; p++) {
6156     PetscInt dof;
6157     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6158     clsize += dof;
6159   }
6160   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6161   /* Filter points */
6162   for (p = 0; p < numPoints * 2; p += 2) {
6163     PetscInt dep;
6164 
6165     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6166     if (dep != depth) continue;
6167     points[Np * 2 + 0] = points[p];
6168     points[Np * 2 + 1] = points[p + 1];
6169     ++Np;
6170   }
6171   /* Get array */
6172   if (!values || !*values) {
6173     PetscInt asize = 0, dof;
6174 
6175     for (p = 0; p < Np * 2; p += 2) {
6176       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6177       asize += dof;
6178     }
6179     if (!values) {
6180       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6181       if (csize) *csize = asize;
6182       PetscFunctionReturn(PETSC_SUCCESS);
6183     }
6184     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6185   } else {
6186     array = *values;
6187   }
6188   PetscCall(VecGetArrayRead(v, &vArray));
6189   /* Get values */
6190   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6191   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6192   /* Cleanup points */
6193   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6194   /* Cleanup array */
6195   PetscCall(VecRestoreArrayRead(v, &vArray));
6196   if (!*values) {
6197     if (csize) *csize = size;
6198     *values = array;
6199   } else {
6200     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6201     *csize = size;
6202   }
6203   PetscFunctionReturn(PETSC_SUCCESS);
6204 }
6205 
6206 /*@C
6207   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6208 
6209   Not collective
6210 
6211   Input Parameters:
6212 + dm      - The `DM`
6213 . section - The section describing the layout in `v`, or `NULL` to use the default section
6214 . v       - The local vector
6215 . point   - The point in the `DM`
6216 . csize   - The number of values in the closure, or `NULL`
6217 - values  - The array of values, which is a borrowed array and should not be freed
6218 
6219   Level: intermediate
6220 
6221   Note:
6222   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6223 
6224   Fortran Notes:
6225   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6226 
6227 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6228 @*/
6229 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6230 {
6231   PetscInt size = 0;
6232 
6233   PetscFunctionBegin;
6234   /* Should work without recalculating size */
6235   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6236   *values = NULL;
6237   PetscFunctionReturn(PETSC_SUCCESS);
6238 }
6239 
6240 static inline void add(PetscScalar *x, PetscScalar y)
6241 {
6242   *x += y;
6243 }
6244 static inline void insert(PetscScalar *x, PetscScalar y)
6245 {
6246   *x = y;
6247 }
6248 
6249 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[])
6250 {
6251   PetscInt        cdof;  /* The number of constraints on this point */
6252   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6253   PetscScalar    *a;
6254   PetscInt        off, cind = 0, k;
6255 
6256   PetscFunctionBegin;
6257   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6258   PetscCall(PetscSectionGetOffset(section, point, &off));
6259   a = &array[off];
6260   if (!cdof || setBC) {
6261     if (clperm) {
6262       if (perm) {
6263         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6264       } else {
6265         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6266       }
6267     } else {
6268       if (perm) {
6269         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6270       } else {
6271         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6272       }
6273     }
6274   } else {
6275     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6276     if (clperm) {
6277       if (perm) {
6278         for (k = 0; k < dof; ++k) {
6279           if ((cind < cdof) && (k == cdofs[cind])) {
6280             ++cind;
6281             continue;
6282           }
6283           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6284         }
6285       } else {
6286         for (k = 0; k < dof; ++k) {
6287           if ((cind < cdof) && (k == cdofs[cind])) {
6288             ++cind;
6289             continue;
6290           }
6291           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6292         }
6293       }
6294     } else {
6295       if (perm) {
6296         for (k = 0; k < dof; ++k) {
6297           if ((cind < cdof) && (k == cdofs[cind])) {
6298             ++cind;
6299             continue;
6300           }
6301           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6302         }
6303       } else {
6304         for (k = 0; k < dof; ++k) {
6305           if ((cind < cdof) && (k == cdofs[cind])) {
6306             ++cind;
6307             continue;
6308           }
6309           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6310         }
6311       }
6312     }
6313   }
6314   PetscFunctionReturn(PETSC_SUCCESS);
6315 }
6316 
6317 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[])
6318 {
6319   PetscInt        cdof;  /* The number of constraints on this point */
6320   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6321   PetscScalar    *a;
6322   PetscInt        off, cind = 0, k;
6323 
6324   PetscFunctionBegin;
6325   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6326   PetscCall(PetscSectionGetOffset(section, point, &off));
6327   a = &array[off];
6328   if (cdof) {
6329     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6330     if (clperm) {
6331       if (perm) {
6332         for (k = 0; k < dof; ++k) {
6333           if ((cind < cdof) && (k == cdofs[cind])) {
6334             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6335             cind++;
6336           }
6337         }
6338       } else {
6339         for (k = 0; k < dof; ++k) {
6340           if ((cind < cdof) && (k == cdofs[cind])) {
6341             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6342             cind++;
6343           }
6344         }
6345       }
6346     } else {
6347       if (perm) {
6348         for (k = 0; k < dof; ++k) {
6349           if ((cind < cdof) && (k == cdofs[cind])) {
6350             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6351             cind++;
6352           }
6353         }
6354       } else {
6355         for (k = 0; k < dof; ++k) {
6356           if ((cind < cdof) && (k == cdofs[cind])) {
6357             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6358             cind++;
6359           }
6360         }
6361       }
6362     }
6363   }
6364   PetscFunctionReturn(PETSC_SUCCESS);
6365 }
6366 
6367 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[])
6368 {
6369   PetscScalar    *a;
6370   PetscInt        fdof, foff, fcdof, foffset = *offset;
6371   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6372   PetscInt        cind = 0, b;
6373 
6374   PetscFunctionBegin;
6375   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6376   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6377   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6378   a = &array[foff];
6379   if (!fcdof || setBC) {
6380     if (clperm) {
6381       if (perm) {
6382         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6383       } else {
6384         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6385       }
6386     } else {
6387       if (perm) {
6388         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6389       } else {
6390         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6391       }
6392     }
6393   } else {
6394     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6395     if (clperm) {
6396       if (perm) {
6397         for (b = 0; b < fdof; b++) {
6398           if ((cind < fcdof) && (b == fcdofs[cind])) {
6399             ++cind;
6400             continue;
6401           }
6402           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6403         }
6404       } else {
6405         for (b = 0; b < fdof; b++) {
6406           if ((cind < fcdof) && (b == fcdofs[cind])) {
6407             ++cind;
6408             continue;
6409           }
6410           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6411         }
6412       }
6413     } else {
6414       if (perm) {
6415         for (b = 0; b < fdof; b++) {
6416           if ((cind < fcdof) && (b == fcdofs[cind])) {
6417             ++cind;
6418             continue;
6419           }
6420           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6421         }
6422       } else {
6423         for (b = 0; b < fdof; b++) {
6424           if ((cind < fcdof) && (b == fcdofs[cind])) {
6425             ++cind;
6426             continue;
6427           }
6428           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6429         }
6430       }
6431     }
6432   }
6433   *offset += fdof;
6434   PetscFunctionReturn(PETSC_SUCCESS);
6435 }
6436 
6437 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[])
6438 {
6439   PetscScalar    *a;
6440   PetscInt        fdof, foff, fcdof, foffset = *offset;
6441   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6442   PetscInt        Nc, cind = 0, ncind = 0, b;
6443   PetscBool       ncSet, fcSet;
6444 
6445   PetscFunctionBegin;
6446   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6447   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6448   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6449   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6450   a = &array[foff];
6451   if (fcdof) {
6452     /* We just override fcdof and fcdofs with Ncc and comps */
6453     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6454     if (clperm) {
6455       if (perm) {
6456         if (comps) {
6457           for (b = 0; b < fdof; b++) {
6458             ncSet = fcSet = PETSC_FALSE;
6459             if (b % Nc == comps[ncind]) {
6460               ncind = (ncind + 1) % Ncc;
6461               ncSet = PETSC_TRUE;
6462             }
6463             if ((cind < fcdof) && (b == fcdofs[cind])) {
6464               ++cind;
6465               fcSet = PETSC_TRUE;
6466             }
6467             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6468           }
6469         } else {
6470           for (b = 0; b < fdof; b++) {
6471             if ((cind < fcdof) && (b == fcdofs[cind])) {
6472               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6473               ++cind;
6474             }
6475           }
6476         }
6477       } else {
6478         if (comps) {
6479           for (b = 0; b < fdof; b++) {
6480             ncSet = fcSet = PETSC_FALSE;
6481             if (b % Nc == comps[ncind]) {
6482               ncind = (ncind + 1) % Ncc;
6483               ncSet = PETSC_TRUE;
6484             }
6485             if ((cind < fcdof) && (b == fcdofs[cind])) {
6486               ++cind;
6487               fcSet = PETSC_TRUE;
6488             }
6489             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6490           }
6491         } else {
6492           for (b = 0; b < fdof; b++) {
6493             if ((cind < fcdof) && (b == fcdofs[cind])) {
6494               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6495               ++cind;
6496             }
6497           }
6498         }
6499       }
6500     } else {
6501       if (perm) {
6502         if (comps) {
6503           for (b = 0; b < fdof; b++) {
6504             ncSet = fcSet = PETSC_FALSE;
6505             if (b % Nc == comps[ncind]) {
6506               ncind = (ncind + 1) % Ncc;
6507               ncSet = PETSC_TRUE;
6508             }
6509             if ((cind < fcdof) && (b == fcdofs[cind])) {
6510               ++cind;
6511               fcSet = PETSC_TRUE;
6512             }
6513             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6514           }
6515         } else {
6516           for (b = 0; b < fdof; b++) {
6517             if ((cind < fcdof) && (b == fcdofs[cind])) {
6518               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6519               ++cind;
6520             }
6521           }
6522         }
6523       } else {
6524         if (comps) {
6525           for (b = 0; b < fdof; b++) {
6526             ncSet = fcSet = PETSC_FALSE;
6527             if (b % Nc == comps[ncind]) {
6528               ncind = (ncind + 1) % Ncc;
6529               ncSet = PETSC_TRUE;
6530             }
6531             if ((cind < fcdof) && (b == fcdofs[cind])) {
6532               ++cind;
6533               fcSet = PETSC_TRUE;
6534             }
6535             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6536           }
6537         } else {
6538           for (b = 0; b < fdof; b++) {
6539             if ((cind < fcdof) && (b == fcdofs[cind])) {
6540               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6541               ++cind;
6542             }
6543           }
6544         }
6545       }
6546     }
6547   }
6548   *offset += fdof;
6549   PetscFunctionReturn(PETSC_SUCCESS);
6550 }
6551 
6552 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6553 {
6554   PetscScalar    *array;
6555   const PetscInt *cone, *coneO;
6556   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6557 
6558   PetscFunctionBeginHot;
6559   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6560   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6561   PetscCall(DMPlexGetCone(dm, point, &cone));
6562   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6563   PetscCall(VecGetArray(v, &array));
6564   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6565     const PetscInt cp = !p ? point : cone[p - 1];
6566     const PetscInt o  = !p ? 0 : coneO[p - 1];
6567 
6568     if ((cp < pStart) || (cp >= pEnd)) {
6569       dof = 0;
6570       continue;
6571     }
6572     PetscCall(PetscSectionGetDof(section, cp, &dof));
6573     /* ADD_VALUES */
6574     {
6575       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6576       PetscScalar    *a;
6577       PetscInt        cdof, coff, cind = 0, k;
6578 
6579       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6580       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6581       a = &array[coff];
6582       if (!cdof) {
6583         if (o >= 0) {
6584           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6585         } else {
6586           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6587         }
6588       } else {
6589         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6590         if (o >= 0) {
6591           for (k = 0; k < dof; ++k) {
6592             if ((cind < cdof) && (k == cdofs[cind])) {
6593               ++cind;
6594               continue;
6595             }
6596             a[k] += values[off + k];
6597           }
6598         } else {
6599           for (k = 0; k < dof; ++k) {
6600             if ((cind < cdof) && (k == cdofs[cind])) {
6601               ++cind;
6602               continue;
6603             }
6604             a[k] += values[off + dof - k - 1];
6605           }
6606         }
6607       }
6608     }
6609   }
6610   PetscCall(VecRestoreArray(v, &array));
6611   PetscFunctionReturn(PETSC_SUCCESS);
6612 }
6613 
6614 /*@C
6615   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6616 
6617   Not collective
6618 
6619   Input Parameters:
6620 + dm      - The `DM`
6621 . section - The section describing the layout in `v`, or `NULL` to use the default section
6622 . v       - The local vector
6623 . point   - The point in the `DM`
6624 . values  - The array of values
6625 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6626          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6627 
6628   Level: intermediate
6629 
6630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6631 @*/
6632 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6633 {
6634   PetscSection    clSection;
6635   IS              clPoints;
6636   PetscScalar    *array;
6637   PetscInt       *points = NULL;
6638   const PetscInt *clp, *clperm = NULL;
6639   PetscInt        depth, numFields, numPoints, p, clsize;
6640 
6641   PetscFunctionBeginHot;
6642   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6643   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6644   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6645   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6646   PetscCall(DMPlexGetDepth(dm, &depth));
6647   PetscCall(PetscSectionGetNumFields(section, &numFields));
6648   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6649     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6650     PetscFunctionReturn(PETSC_SUCCESS);
6651   }
6652   /* Get points */
6653   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6654   for (clsize = 0, p = 0; p < numPoints; p++) {
6655     PetscInt dof;
6656     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6657     clsize += dof;
6658   }
6659   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6660   /* Get array */
6661   PetscCall(VecGetArray(v, &array));
6662   /* Get values */
6663   if (numFields > 0) {
6664     PetscInt offset = 0, f;
6665     for (f = 0; f < numFields; ++f) {
6666       const PetscInt    **perms = NULL;
6667       const PetscScalar **flips = NULL;
6668 
6669       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6670       switch (mode) {
6671       case INSERT_VALUES:
6672         for (p = 0; p < numPoints; p++) {
6673           const PetscInt     point = points[2 * p];
6674           const PetscInt    *perm  = perms ? perms[p] : NULL;
6675           const PetscScalar *flip  = flips ? flips[p] : NULL;
6676           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6677         }
6678         break;
6679       case INSERT_ALL_VALUES:
6680         for (p = 0; p < numPoints; p++) {
6681           const PetscInt     point = points[2 * p];
6682           const PetscInt    *perm  = perms ? perms[p] : NULL;
6683           const PetscScalar *flip  = flips ? flips[p] : NULL;
6684           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6685         }
6686         break;
6687       case INSERT_BC_VALUES:
6688         for (p = 0; p < numPoints; p++) {
6689           const PetscInt     point = points[2 * p];
6690           const PetscInt    *perm  = perms ? perms[p] : NULL;
6691           const PetscScalar *flip  = flips ? flips[p] : NULL;
6692           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6693         }
6694         break;
6695       case ADD_VALUES:
6696         for (p = 0; p < numPoints; p++) {
6697           const PetscInt     point = points[2 * p];
6698           const PetscInt    *perm  = perms ? perms[p] : NULL;
6699           const PetscScalar *flip  = flips ? flips[p] : NULL;
6700           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6701         }
6702         break;
6703       case ADD_ALL_VALUES:
6704         for (p = 0; p < numPoints; p++) {
6705           const PetscInt     point = points[2 * p];
6706           const PetscInt    *perm  = perms ? perms[p] : NULL;
6707           const PetscScalar *flip  = flips ? flips[p] : NULL;
6708           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6709         }
6710         break;
6711       case ADD_BC_VALUES:
6712         for (p = 0; p < numPoints; p++) {
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(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6717         }
6718         break;
6719       default:
6720         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6721       }
6722       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6723     }
6724   } else {
6725     PetscInt            dof, off;
6726     const PetscInt    **perms = NULL;
6727     const PetscScalar **flips = NULL;
6728 
6729     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6730     switch (mode) {
6731     case INSERT_VALUES:
6732       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6733         const PetscInt     point = points[2 * p];
6734         const PetscInt    *perm  = perms ? perms[p] : NULL;
6735         const PetscScalar *flip  = flips ? flips[p] : NULL;
6736         PetscCall(PetscSectionGetDof(section, point, &dof));
6737         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6738       }
6739       break;
6740     case INSERT_ALL_VALUES:
6741       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6742         const PetscInt     point = points[2 * p];
6743         const PetscInt    *perm  = perms ? perms[p] : NULL;
6744         const PetscScalar *flip  = flips ? flips[p] : NULL;
6745         PetscCall(PetscSectionGetDof(section, point, &dof));
6746         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6747       }
6748       break;
6749     case INSERT_BC_VALUES:
6750       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6751         const PetscInt     point = points[2 * p];
6752         const PetscInt    *perm  = perms ? perms[p] : NULL;
6753         const PetscScalar *flip  = flips ? flips[p] : NULL;
6754         PetscCall(PetscSectionGetDof(section, point, &dof));
6755         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6756       }
6757       break;
6758     case ADD_VALUES:
6759       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6760         const PetscInt     point = points[2 * p];
6761         const PetscInt    *perm  = perms ? perms[p] : NULL;
6762         const PetscScalar *flip  = flips ? flips[p] : NULL;
6763         PetscCall(PetscSectionGetDof(section, point, &dof));
6764         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6765       }
6766       break;
6767     case ADD_ALL_VALUES:
6768       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6769         const PetscInt     point = points[2 * p];
6770         const PetscInt    *perm  = perms ? perms[p] : NULL;
6771         const PetscScalar *flip  = flips ? flips[p] : NULL;
6772         PetscCall(PetscSectionGetDof(section, point, &dof));
6773         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6774       }
6775       break;
6776     case ADD_BC_VALUES:
6777       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6778         const PetscInt     point = points[2 * p];
6779         const PetscInt    *perm  = perms ? perms[p] : NULL;
6780         const PetscScalar *flip  = flips ? flips[p] : NULL;
6781         PetscCall(PetscSectionGetDof(section, point, &dof));
6782         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6783       }
6784       break;
6785     default:
6786       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6787     }
6788     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6789   }
6790   /* Cleanup points */
6791   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6792   /* Cleanup array */
6793   PetscCall(VecRestoreArray(v, &array));
6794   PetscFunctionReturn(PETSC_SUCCESS);
6795 }
6796 
6797 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6798 {
6799   const PetscInt *supp, *cone;
6800   PetscScalar    *a;
6801   PetscInt        dim, Ns, dof, off, n = 0;
6802 
6803   PetscFunctionBegin;
6804   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6805   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6806   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6807   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6808   if (PetscDefined(USE_DEBUG)) {
6809     PetscInt vStart, vEnd;
6810 
6811     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6812     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);
6813   }
6814   PetscAssertPointer(values, 5);
6815 
6816   PetscCall(DMGetDimension(dm, &dim));
6817   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6818   PetscCall(DMPlexGetSupport(dm, point, &supp));
6819   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);
6820   PetscCall(VecGetArray(v, &a));
6821   PetscCall(PetscSectionGetDof(section, point, &dof));
6822   PetscCall(PetscSectionGetOffset(section, point, &off));
6823   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6824   for (PetscInt d = 0; d < dim; ++d) {
6825     // Left edge
6826     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6827     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6828     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6829     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6830     // Right edge
6831     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6832     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6833     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6834     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6835   }
6836   PetscCall(VecRestoreArray(v, &a));
6837   PetscFunctionReturn(PETSC_SUCCESS);
6838 }
6839 
6840 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6841 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6842 {
6843   PetscFunctionBegin;
6844   *contains = PETSC_TRUE;
6845   if (label) {
6846     PetscInt fdof;
6847 
6848     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6849     if (!*contains) {
6850       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6851       *offset += fdof;
6852       PetscFunctionReturn(PETSC_SUCCESS);
6853     }
6854   }
6855   PetscFunctionReturn(PETSC_SUCCESS);
6856 }
6857 
6858 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6859 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)
6860 {
6861   PetscSection    clSection;
6862   IS              clPoints;
6863   PetscScalar    *array;
6864   PetscInt       *points = NULL;
6865   const PetscInt *clp;
6866   PetscInt        numFields, numPoints, p;
6867   PetscInt        offset = 0, f;
6868 
6869   PetscFunctionBeginHot;
6870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6871   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6872   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6873   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6874   PetscCall(PetscSectionGetNumFields(section, &numFields));
6875   /* Get points */
6876   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6877   /* Get array */
6878   PetscCall(VecGetArray(v, &array));
6879   /* Get values */
6880   for (f = 0; f < numFields; ++f) {
6881     const PetscInt    **perms = NULL;
6882     const PetscScalar **flips = NULL;
6883     PetscBool           contains;
6884 
6885     if (!fieldActive[f]) {
6886       for (p = 0; p < numPoints * 2; p += 2) {
6887         PetscInt fdof;
6888         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6889         offset += fdof;
6890       }
6891       continue;
6892     }
6893     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6894     switch (mode) {
6895     case INSERT_VALUES:
6896       for (p = 0; p < numPoints; p++) {
6897         const PetscInt     point = points[2 * p];
6898         const PetscInt    *perm  = perms ? perms[p] : NULL;
6899         const PetscScalar *flip  = flips ? flips[p] : NULL;
6900         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6901         if (!contains) continue;
6902         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6903       }
6904       break;
6905     case INSERT_ALL_VALUES:
6906       for (p = 0; p < numPoints; p++) {
6907         const PetscInt     point = points[2 * p];
6908         const PetscInt    *perm  = perms ? perms[p] : NULL;
6909         const PetscScalar *flip  = flips ? flips[p] : NULL;
6910         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6911         if (!contains) continue;
6912         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6913       }
6914       break;
6915     case INSERT_BC_VALUES:
6916       for (p = 0; p < numPoints; p++) {
6917         const PetscInt     point = points[2 * p];
6918         const PetscInt    *perm  = perms ? perms[p] : NULL;
6919         const PetscScalar *flip  = flips ? flips[p] : NULL;
6920         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6921         if (!contains) continue;
6922         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6923       }
6924       break;
6925     case ADD_VALUES:
6926       for (p = 0; p < numPoints; p++) {
6927         const PetscInt     point = points[2 * p];
6928         const PetscInt    *perm  = perms ? perms[p] : NULL;
6929         const PetscScalar *flip  = flips ? flips[p] : NULL;
6930         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6931         if (!contains) continue;
6932         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6933       }
6934       break;
6935     case ADD_ALL_VALUES:
6936       for (p = 0; p < numPoints; p++) {
6937         const PetscInt     point = points[2 * p];
6938         const PetscInt    *perm  = perms ? perms[p] : NULL;
6939         const PetscScalar *flip  = flips ? flips[p] : NULL;
6940         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6941         if (!contains) continue;
6942         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6943       }
6944       break;
6945     default:
6946       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6947     }
6948     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6949   }
6950   /* Cleanup points */
6951   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6952   /* Cleanup array */
6953   PetscCall(VecRestoreArray(v, &array));
6954   PetscFunctionReturn(PETSC_SUCCESS);
6955 }
6956 
6957 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6958 {
6959   PetscMPIInt rank;
6960   PetscInt    i, j;
6961 
6962   PetscFunctionBegin;
6963   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6964   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6965   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6966   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6967   numCIndices = numCIndices ? numCIndices : numRIndices;
6968   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6969   for (i = 0; i < numRIndices; i++) {
6970     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6971     for (j = 0; j < numCIndices; j++) {
6972 #if defined(PETSC_USE_COMPLEX)
6973       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6974 #else
6975       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6976 #endif
6977     }
6978     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6979   }
6980   PetscFunctionReturn(PETSC_SUCCESS);
6981 }
6982 
6983 /*
6984   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6985 
6986   Input Parameters:
6987 + section - The section for this data layout
6988 . islocal - Is the section (and thus indices being requested) local or global?
6989 . point   - The point contributing dofs with these indices
6990 . off     - The global offset of this point
6991 . loff    - The local offset of each field
6992 . setBC   - The flag determining whether to include indices of boundary values
6993 . perm    - A permutation of the dofs on this point, or NULL
6994 - indperm - A permutation of the entire indices array, or NULL
6995 
6996   Output Parameter:
6997 . indices - Indices for dofs on this point
6998 
6999   Level: developer
7000 
7001   Note: The indices could be local or global, depending on the value of 'off'.
7002 */
7003 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7004 {
7005   PetscInt        dof;   /* The number of unknowns on this point */
7006   PetscInt        cdof;  /* The number of constraints on this point */
7007   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7008   PetscInt        cind = 0, k;
7009 
7010   PetscFunctionBegin;
7011   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7012   PetscCall(PetscSectionGetDof(section, point, &dof));
7013   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7014   if (!cdof || setBC) {
7015     for (k = 0; k < dof; ++k) {
7016       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7017       const PetscInt ind    = indperm ? indperm[preind] : preind;
7018 
7019       indices[ind] = off + k;
7020     }
7021   } else {
7022     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7023     for (k = 0; k < dof; ++k) {
7024       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7025       const PetscInt ind    = indperm ? indperm[preind] : preind;
7026 
7027       if ((cind < cdof) && (k == cdofs[cind])) {
7028         /* Insert check for returning constrained indices */
7029         indices[ind] = -(off + k + 1);
7030         ++cind;
7031       } else {
7032         indices[ind] = off + k - (islocal ? 0 : cind);
7033       }
7034     }
7035   }
7036   *loff += dof;
7037   PetscFunctionReturn(PETSC_SUCCESS);
7038 }
7039 
7040 /*
7041  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7042 
7043  Input Parameters:
7044 + section - a section (global or local)
7045 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7046 . point - point within section
7047 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7048 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7049 . setBC - identify constrained (boundary condition) points via involution.
7050 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7051 . permsoff - offset
7052 - indperm - index permutation
7053 
7054  Output Parameter:
7055 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7056 . indices - array to hold indices (as defined by section) of each dof associated with point
7057 
7058  Notes:
7059  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7060  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7061  in the local vector.
7062 
7063  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7064  significant).  It is invalid to call with a global section and setBC=true.
7065 
7066  Developer Note:
7067  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7068  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7069  offset could be obtained from the section instead of passing it explicitly as we do now.
7070 
7071  Example:
7072  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7073  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7074  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7075  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.
7076 
7077  Level: developer
7078 */
7079 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[])
7080 {
7081   PetscInt numFields, foff, f;
7082 
7083   PetscFunctionBegin;
7084   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7085   PetscCall(PetscSectionGetNumFields(section, &numFields));
7086   for (f = 0, foff = 0; f < numFields; ++f) {
7087     PetscInt        fdof, cfdof;
7088     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7089     PetscInt        cind = 0, b;
7090     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7091 
7092     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7093     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7094     if (!cfdof || setBC) {
7095       for (b = 0; b < fdof; ++b) {
7096         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7097         const PetscInt ind    = indperm ? indperm[preind] : preind;
7098 
7099         indices[ind] = off + foff + b;
7100       }
7101     } else {
7102       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7103       for (b = 0; b < fdof; ++b) {
7104         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7105         const PetscInt ind    = indperm ? indperm[preind] : preind;
7106 
7107         if ((cind < cfdof) && (b == fcdofs[cind])) {
7108           indices[ind] = -(off + foff + b + 1);
7109           ++cind;
7110         } else {
7111           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7112         }
7113       }
7114     }
7115     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7116     foffs[f] += fdof;
7117   }
7118   PetscFunctionReturn(PETSC_SUCCESS);
7119 }
7120 
7121 /*
7122   This version believes the globalSection offsets for each field, rather than just the point offset
7123 
7124  . foffs - The offset into 'indices' for each field, since it is segregated by field
7125 
7126  Notes:
7127  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7128  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7129 */
7130 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7131 {
7132   PetscInt numFields, foff, f;
7133 
7134   PetscFunctionBegin;
7135   PetscCall(PetscSectionGetNumFields(section, &numFields));
7136   for (f = 0; f < numFields; ++f) {
7137     PetscInt        fdof, cfdof;
7138     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7139     PetscInt        cind = 0, b;
7140     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7141 
7142     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7143     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7144     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7145     if (!cfdof) {
7146       for (b = 0; b < fdof; ++b) {
7147         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7148         const PetscInt ind    = indperm ? indperm[preind] : preind;
7149 
7150         indices[ind] = foff + b;
7151       }
7152     } else {
7153       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7154       for (b = 0; b < fdof; ++b) {
7155         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7156         const PetscInt ind    = indperm ? indperm[preind] : preind;
7157 
7158         if ((cind < cfdof) && (b == fcdofs[cind])) {
7159           indices[ind] = -(foff + b + 1);
7160           ++cind;
7161         } else {
7162           indices[ind] = foff + b - cind;
7163         }
7164       }
7165     }
7166     foffs[f] += fdof;
7167   }
7168   PetscFunctionReturn(PETSC_SUCCESS);
7169 }
7170 
7171 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)
7172 {
7173   Mat             cMat;
7174   PetscSection    aSec, cSec;
7175   IS              aIS;
7176   PetscInt        aStart = -1, aEnd = -1;
7177   const PetscInt *anchors;
7178   PetscInt        numFields, f, p, q, newP = 0;
7179   PetscInt        newNumPoints = 0, newNumIndices = 0;
7180   PetscInt       *newPoints, *indices, *newIndices;
7181   PetscInt        maxAnchor, maxDof;
7182   PetscInt        newOffsets[32];
7183   PetscInt       *pointMatOffsets[32];
7184   PetscInt       *newPointOffsets[32];
7185   PetscScalar    *pointMat[32];
7186   PetscScalar    *newValues      = NULL, *tmpValues;
7187   PetscBool       anyConstrained = PETSC_FALSE;
7188 
7189   PetscFunctionBegin;
7190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7191   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7192   PetscCall(PetscSectionGetNumFields(section, &numFields));
7193 
7194   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7195   /* if there are point-to-point constraints */
7196   if (aSec) {
7197     PetscCall(PetscArrayzero(newOffsets, 32));
7198     PetscCall(ISGetIndices(aIS, &anchors));
7199     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7200     /* figure out how many points are going to be in the new element matrix
7201      * (we allow double counting, because it's all just going to be summed
7202      * into the global matrix anyway) */
7203     for (p = 0; p < 2 * numPoints; p += 2) {
7204       PetscInt b    = points[p];
7205       PetscInt bDof = 0, bSecDof;
7206 
7207       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7208       if (!bSecDof) continue;
7209       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7210       if (bDof) {
7211         /* this point is constrained */
7212         /* it is going to be replaced by its anchors */
7213         PetscInt bOff, q;
7214 
7215         anyConstrained = PETSC_TRUE;
7216         newNumPoints += bDof;
7217         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7218         for (q = 0; q < bDof; q++) {
7219           PetscInt a = anchors[bOff + q];
7220           PetscInt aDof;
7221 
7222           PetscCall(PetscSectionGetDof(section, a, &aDof));
7223           newNumIndices += aDof;
7224           for (f = 0; f < numFields; ++f) {
7225             PetscInt fDof;
7226 
7227             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7228             newOffsets[f + 1] += fDof;
7229           }
7230         }
7231       } else {
7232         /* this point is not constrained */
7233         newNumPoints++;
7234         newNumIndices += bSecDof;
7235         for (f = 0; f < numFields; ++f) {
7236           PetscInt fDof;
7237 
7238           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7239           newOffsets[f + 1] += fDof;
7240         }
7241       }
7242     }
7243   }
7244   if (!anyConstrained) {
7245     if (outNumPoints) *outNumPoints = 0;
7246     if (outNumIndices) *outNumIndices = 0;
7247     if (outPoints) *outPoints = NULL;
7248     if (outValues) *outValues = NULL;
7249     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7250     PetscFunctionReturn(PETSC_SUCCESS);
7251   }
7252 
7253   if (outNumPoints) *outNumPoints = newNumPoints;
7254   if (outNumIndices) *outNumIndices = newNumIndices;
7255 
7256   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7257 
7258   if (!outPoints && !outValues) {
7259     if (offsets) {
7260       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7261     }
7262     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7263     PetscFunctionReturn(PETSC_SUCCESS);
7264   }
7265 
7266   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7267 
7268   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7269 
7270   /* workspaces */
7271   if (numFields) {
7272     for (f = 0; f < numFields; f++) {
7273       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7274       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7275     }
7276   } else {
7277     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7278     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7279   }
7280 
7281   /* get workspaces for the point-to-point matrices */
7282   if (numFields) {
7283     PetscInt totalOffset, totalMatOffset;
7284 
7285     for (p = 0; p < numPoints; p++) {
7286       PetscInt b    = points[2 * p];
7287       PetscInt bDof = 0, bSecDof;
7288 
7289       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7290       if (!bSecDof) {
7291         for (f = 0; f < numFields; f++) {
7292           newPointOffsets[f][p + 1] = 0;
7293           pointMatOffsets[f][p + 1] = 0;
7294         }
7295         continue;
7296       }
7297       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7298       if (bDof) {
7299         for (f = 0; f < numFields; f++) {
7300           PetscInt fDof, q, bOff, allFDof = 0;
7301 
7302           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7303           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7304           for (q = 0; q < bDof; q++) {
7305             PetscInt a = anchors[bOff + q];
7306             PetscInt aFDof;
7307 
7308             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7309             allFDof += aFDof;
7310           }
7311           newPointOffsets[f][p + 1] = allFDof;
7312           pointMatOffsets[f][p + 1] = fDof * allFDof;
7313         }
7314       } else {
7315         for (f = 0; f < numFields; f++) {
7316           PetscInt fDof;
7317 
7318           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7319           newPointOffsets[f][p + 1] = fDof;
7320           pointMatOffsets[f][p + 1] = 0;
7321         }
7322       }
7323     }
7324     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7325       newPointOffsets[f][0] = totalOffset;
7326       pointMatOffsets[f][0] = totalMatOffset;
7327       for (p = 0; p < numPoints; p++) {
7328         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7329         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7330       }
7331       totalOffset    = newPointOffsets[f][numPoints];
7332       totalMatOffset = pointMatOffsets[f][numPoints];
7333       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7334     }
7335   } else {
7336     for (p = 0; p < numPoints; p++) {
7337       PetscInt b    = points[2 * p];
7338       PetscInt bDof = 0, bSecDof;
7339 
7340       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7341       if (!bSecDof) {
7342         newPointOffsets[0][p + 1] = 0;
7343         pointMatOffsets[0][p + 1] = 0;
7344         continue;
7345       }
7346       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7347       if (bDof) {
7348         PetscInt bOff, q, allDof = 0;
7349 
7350         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7351         for (q = 0; q < bDof; q++) {
7352           PetscInt a = anchors[bOff + q], aDof;
7353 
7354           PetscCall(PetscSectionGetDof(section, a, &aDof));
7355           allDof += aDof;
7356         }
7357         newPointOffsets[0][p + 1] = allDof;
7358         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7359       } else {
7360         newPointOffsets[0][p + 1] = bSecDof;
7361         pointMatOffsets[0][p + 1] = 0;
7362       }
7363     }
7364     newPointOffsets[0][0] = 0;
7365     pointMatOffsets[0][0] = 0;
7366     for (p = 0; p < numPoints; p++) {
7367       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7368       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7369     }
7370     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7371   }
7372 
7373   /* output arrays */
7374   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7375 
7376   /* get the point-to-point matrices; construct newPoints */
7377   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7378   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7379   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7380   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7381   if (numFields) {
7382     for (p = 0, newP = 0; p < numPoints; p++) {
7383       PetscInt b    = points[2 * p];
7384       PetscInt o    = points[2 * p + 1];
7385       PetscInt bDof = 0, bSecDof;
7386 
7387       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7388       if (!bSecDof) continue;
7389       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7390       if (bDof) {
7391         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7392 
7393         fStart[0] = 0;
7394         fEnd[0]   = 0;
7395         for (f = 0; f < numFields; f++) {
7396           PetscInt fDof;
7397 
7398           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7399           fStart[f + 1] = fStart[f] + fDof;
7400           fEnd[f + 1]   = fStart[f + 1];
7401         }
7402         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7403         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7404 
7405         fAnchorStart[0] = 0;
7406         fAnchorEnd[0]   = 0;
7407         for (f = 0; f < numFields; f++) {
7408           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7409 
7410           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7411           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7412         }
7413         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7414         for (q = 0; q < bDof; q++) {
7415           PetscInt a = anchors[bOff + q], aOff;
7416 
7417           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7418           newPoints[2 * (newP + q)]     = a;
7419           newPoints[2 * (newP + q) + 1] = 0;
7420           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7421           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7422         }
7423         newP += bDof;
7424 
7425         if (outValues) {
7426           /* get the point-to-point submatrix */
7427           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]));
7428         }
7429       } else {
7430         newPoints[2 * newP]     = b;
7431         newPoints[2 * newP + 1] = o;
7432         newP++;
7433       }
7434     }
7435   } else {
7436     for (p = 0; p < numPoints; p++) {
7437       PetscInt b    = points[2 * p];
7438       PetscInt o    = points[2 * p + 1];
7439       PetscInt bDof = 0, bSecDof;
7440 
7441       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7442       if (!bSecDof) continue;
7443       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7444       if (bDof) {
7445         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7446 
7447         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7448         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7449 
7450         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7451         for (q = 0; q < bDof; q++) {
7452           PetscInt a = anchors[bOff + q], aOff;
7453 
7454           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7455 
7456           newPoints[2 * (newP + q)]     = a;
7457           newPoints[2 * (newP + q) + 1] = 0;
7458           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7459           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7460         }
7461         newP += bDof;
7462 
7463         /* get the point-to-point submatrix */
7464         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7465       } else {
7466         newPoints[2 * newP]     = b;
7467         newPoints[2 * newP + 1] = o;
7468         newP++;
7469       }
7470     }
7471   }
7472 
7473   if (outValues) {
7474     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7475     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7476     /* multiply constraints on the right */
7477     if (numFields) {
7478       for (f = 0; f < numFields; f++) {
7479         PetscInt oldOff = offsets[f];
7480 
7481         for (p = 0; p < numPoints; p++) {
7482           PetscInt cStart = newPointOffsets[f][p];
7483           PetscInt b      = points[2 * p];
7484           PetscInt c, r, k;
7485           PetscInt dof;
7486 
7487           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7488           if (!dof) continue;
7489           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7490             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7491             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7492 
7493             for (r = 0; r < numIndices; r++) {
7494               for (c = 0; c < nCols; c++) {
7495                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7496               }
7497             }
7498           } else {
7499             /* copy this column as is */
7500             for (r = 0; r < numIndices; r++) {
7501               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7502             }
7503           }
7504           oldOff += dof;
7505         }
7506       }
7507     } else {
7508       PetscInt oldOff = 0;
7509       for (p = 0; p < numPoints; p++) {
7510         PetscInt cStart = newPointOffsets[0][p];
7511         PetscInt b      = points[2 * p];
7512         PetscInt c, r, k;
7513         PetscInt dof;
7514 
7515         PetscCall(PetscSectionGetDof(section, b, &dof));
7516         if (!dof) continue;
7517         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7518           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7519           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7520 
7521           for (r = 0; r < numIndices; r++) {
7522             for (c = 0; c < nCols; c++) {
7523               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7524             }
7525           }
7526         } else {
7527           /* copy this column as is */
7528           for (r = 0; r < numIndices; r++) {
7529             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7530           }
7531         }
7532         oldOff += dof;
7533       }
7534     }
7535 
7536     if (multiplyLeft) {
7537       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7538       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7539       /* multiply constraints transpose on the left */
7540       if (numFields) {
7541         for (f = 0; f < numFields; f++) {
7542           PetscInt oldOff = offsets[f];
7543 
7544           for (p = 0; p < numPoints; p++) {
7545             PetscInt rStart = newPointOffsets[f][p];
7546             PetscInt b      = points[2 * p];
7547             PetscInt c, r, k;
7548             PetscInt dof;
7549 
7550             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7551             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7552               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7553               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7554 
7555               for (r = 0; r < nRows; r++) {
7556                 for (c = 0; c < newNumIndices; c++) {
7557                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7558                 }
7559               }
7560             } else {
7561               /* copy this row as is */
7562               for (r = 0; r < dof; r++) {
7563                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7564               }
7565             }
7566             oldOff += dof;
7567           }
7568         }
7569       } else {
7570         PetscInt oldOff = 0;
7571 
7572         for (p = 0; p < numPoints; p++) {
7573           PetscInt rStart = newPointOffsets[0][p];
7574           PetscInt b      = points[2 * p];
7575           PetscInt c, r, k;
7576           PetscInt dof;
7577 
7578           PetscCall(PetscSectionGetDof(section, b, &dof));
7579           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7580             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7581             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7582 
7583             for (r = 0; r < nRows; r++) {
7584               for (c = 0; c < newNumIndices; c++) {
7585                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7586               }
7587             }
7588           } else {
7589             /* copy this row as is */
7590             for (r = 0; r < dof; r++) {
7591               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7592             }
7593           }
7594           oldOff += dof;
7595         }
7596       }
7597 
7598       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7599     } else {
7600       newValues = tmpValues;
7601     }
7602   }
7603 
7604   /* clean up */
7605   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7606   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7607 
7608   if (numFields) {
7609     for (f = 0; f < numFields; f++) {
7610       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7611       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7612       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7613     }
7614   } else {
7615     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7616     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7617     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7618   }
7619   PetscCall(ISRestoreIndices(aIS, &anchors));
7620 
7621   /* output */
7622   if (outPoints) {
7623     *outPoints = newPoints;
7624   } else {
7625     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7626   }
7627   if (outValues) *outValues = newValues;
7628   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7629   PetscFunctionReturn(PETSC_SUCCESS);
7630 }
7631 
7632 /*@C
7633   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7634 
7635   Not collective
7636 
7637   Input Parameters:
7638 + dm         - The `DM`
7639 . section    - The `PetscSection` describing the points (a local section)
7640 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7641 . point      - The point defining the closure
7642 - useClPerm  - Use the closure point permutation if available
7643 
7644   Output Parameters:
7645 + numIndices - The number of dof indices in the closure of point with the input sections
7646 . indices    - The dof indices
7647 . outOffsets - Array to write the field offsets into, or `NULL`
7648 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7649 
7650   Level: advanced
7651 
7652   Notes:
7653   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7654 
7655   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7656   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7657   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7658   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7659   indices (with the above semantics) are implied.
7660 
7661 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7662           `PetscSection`, `DMGetGlobalSection()`
7663 @*/
7664 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7665 {
7666   /* Closure ordering */
7667   PetscSection    clSection;
7668   IS              clPoints;
7669   const PetscInt *clp;
7670   PetscInt       *points;
7671   const PetscInt *clperm = NULL;
7672   /* Dof permutation and sign flips */
7673   const PetscInt    **perms[32] = {NULL};
7674   const PetscScalar **flips[32] = {NULL};
7675   PetscScalar        *valCopy   = NULL;
7676   /* Hanging node constraints */
7677   PetscInt    *pointsC = NULL;
7678   PetscScalar *valuesC = NULL;
7679   PetscInt     NclC, NiC;
7680 
7681   PetscInt *idx;
7682   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7683   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7684 
7685   PetscFunctionBeginHot;
7686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7687   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7688   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7689   if (numIndices) PetscAssertPointer(numIndices, 6);
7690   if (indices) PetscAssertPointer(indices, 7);
7691   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7692   if (values) PetscAssertPointer(values, 9);
7693   PetscCall(PetscSectionGetNumFields(section, &Nf));
7694   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7695   PetscCall(PetscArrayzero(offsets, 32));
7696   /* 1) Get points in closure */
7697   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7698   if (useClPerm) {
7699     PetscInt depth, clsize;
7700     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7701     for (clsize = 0, p = 0; p < Ncl; p++) {
7702       PetscInt dof;
7703       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7704       clsize += dof;
7705     }
7706     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7707   }
7708   /* 2) Get number of indices on these points and field offsets from section */
7709   for (p = 0; p < Ncl * 2; p += 2) {
7710     PetscInt dof, fdof;
7711 
7712     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7713     for (f = 0; f < Nf; ++f) {
7714       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7715       offsets[f + 1] += fdof;
7716     }
7717     Ni += dof;
7718   }
7719   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7720   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7721   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7722   for (f = 0; f < PetscMax(1, Nf); ++f) {
7723     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7724     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7725     /* may need to apply sign changes to the element matrix */
7726     if (values && flips[f]) {
7727       PetscInt foffset = offsets[f];
7728 
7729       for (p = 0; p < Ncl; ++p) {
7730         PetscInt           pnt  = points[2 * p], fdof;
7731         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7732 
7733         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7734         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7735         if (flip) {
7736           PetscInt i, j, k;
7737 
7738           if (!valCopy) {
7739             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7740             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7741             *values = valCopy;
7742           }
7743           for (i = 0; i < fdof; ++i) {
7744             PetscScalar fval = flip[i];
7745 
7746             for (k = 0; k < Ni; ++k) {
7747               valCopy[Ni * (foffset + i) + k] *= fval;
7748               valCopy[Ni * k + (foffset + i)] *= fval;
7749             }
7750           }
7751         }
7752         foffset += fdof;
7753       }
7754     }
7755   }
7756   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7757   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7758   if (NclC) {
7759     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7760     for (f = 0; f < PetscMax(1, Nf); ++f) {
7761       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7762       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7763     }
7764     for (f = 0; f < PetscMax(1, Nf); ++f) {
7765       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7766       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7767     }
7768     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7769     Ncl    = NclC;
7770     Ni     = NiC;
7771     points = pointsC;
7772     if (values) *values = valuesC;
7773   }
7774   /* 5) Calculate indices */
7775   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7776   if (Nf) {
7777     PetscInt  idxOff;
7778     PetscBool useFieldOffsets;
7779 
7780     if (outOffsets) {
7781       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7782     }
7783     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7784     if (useFieldOffsets) {
7785       for (p = 0; p < Ncl; ++p) {
7786         const PetscInt pnt = points[p * 2];
7787 
7788         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7789       }
7790     } else {
7791       for (p = 0; p < Ncl; ++p) {
7792         const PetscInt pnt = points[p * 2];
7793 
7794         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7795         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7796          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7797          * global section. */
7798         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7799       }
7800     }
7801   } else {
7802     PetscInt off = 0, idxOff;
7803 
7804     for (p = 0; p < Ncl; ++p) {
7805       const PetscInt  pnt  = points[p * 2];
7806       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7807 
7808       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7809       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7810        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7811       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7812     }
7813   }
7814   /* 6) Cleanup */
7815   for (f = 0; f < PetscMax(1, Nf); ++f) {
7816     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7817     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7818   }
7819   if (NclC) {
7820     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7821   } else {
7822     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7823   }
7824 
7825   if (numIndices) *numIndices = Ni;
7826   if (indices) *indices = idx;
7827   PetscFunctionReturn(PETSC_SUCCESS);
7828 }
7829 
7830 /*@C
7831   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7832 
7833   Not collective
7834 
7835   Input Parameters:
7836 + dm         - The `DM`
7837 . section    - The `PetscSection` describing the points (a local section)
7838 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7839 . point      - The point defining the closure
7840 - useClPerm  - Use the closure point permutation if available
7841 
7842   Output Parameters:
7843 + numIndices - The number of dof indices in the closure of point with the input sections
7844 . indices    - The dof indices
7845 . outOffsets - Array to write the field offsets into, or `NULL`
7846 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7847 
7848   Level: advanced
7849 
7850   Notes:
7851   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7852 
7853   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7854   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7855   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7856   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7857   indices (with the above semantics) are implied.
7858 
7859 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7860 @*/
7861 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7862 {
7863   PetscFunctionBegin;
7864   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7865   PetscAssertPointer(indices, 7);
7866   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7867   PetscFunctionReturn(PETSC_SUCCESS);
7868 }
7869 
7870 /*@C
7871   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7872 
7873   Not collective
7874 
7875   Input Parameters:
7876 + dm            - The `DM`
7877 . section       - The section describing the layout in `v`, or `NULL` to use the default section
7878 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7879 . A             - The matrix
7880 . point         - The point in the `DM`
7881 . values        - The array of values
7882 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7883 
7884   Level: intermediate
7885 
7886 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7887 @*/
7888 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7889 {
7890   DM_Plex           *mesh = (DM_Plex *)dm->data;
7891   PetscInt          *indices;
7892   PetscInt           numIndices;
7893   const PetscScalar *valuesOrig = values;
7894   PetscErrorCode     ierr;
7895 
7896   PetscFunctionBegin;
7897   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7898   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7899   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7900   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7901   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7902   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7903 
7904   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7905 
7906   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7907   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7908   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7909   if (ierr) {
7910     PetscMPIInt rank;
7911 
7912     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7913     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7914     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7915     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7916     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7917     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7918   }
7919   if (mesh->printFEM > 1) {
7920     PetscInt i;
7921     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7922     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7923     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7924   }
7925 
7926   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7927   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7928   PetscFunctionReturn(PETSC_SUCCESS);
7929 }
7930 
7931 /*@C
7932   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
7933 
7934   Not collective
7935 
7936   Input Parameters:
7937 + dmRow            - The `DM` for the row fields
7938 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
7939 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7940 . dmCol            - The `DM` for the column fields
7941 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
7942 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7943 . A                - The matrix
7944 . point            - The point in the `DM`
7945 . values           - The array of values
7946 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7947 
7948   Level: intermediate
7949 
7950 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7951 @*/
7952 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7953 {
7954   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7955   PetscInt          *indicesRow, *indicesCol;
7956   PetscInt           numIndicesRow, numIndicesCol;
7957   const PetscScalar *valuesOrig = values;
7958   PetscErrorCode     ierr;
7959 
7960   PetscFunctionBegin;
7961   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7962   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7963   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7964   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7965   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7966   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7967   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7968   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7969   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7970   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7971   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7972 
7973   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7974   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7975 
7976   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7977   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7978   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7979   if (ierr) {
7980     PetscMPIInt rank;
7981 
7982     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7983     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7984     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7985     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7986     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7987     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7988   }
7989 
7990   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7991   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7992   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7993   PetscFunctionReturn(PETSC_SUCCESS);
7994 }
7995 
7996 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7997 {
7998   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7999   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8000   PetscInt       *cpoints = NULL;
8001   PetscInt       *findices, *cindices;
8002   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8003   PetscInt        foffsets[32], coffsets[32];
8004   DMPolytopeType  ct;
8005   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8006   PetscErrorCode  ierr;
8007 
8008   PetscFunctionBegin;
8009   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8010   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8011   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8012   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8013   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8014   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8015   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8016   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8017   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8018   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8019   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8020   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8021   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8022   PetscCall(PetscArrayzero(foffsets, 32));
8023   PetscCall(PetscArrayzero(coffsets, 32));
8024   /* Column indices */
8025   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8026   maxFPoints = numCPoints;
8027   /* Compress out points not in the section */
8028   /*   TODO: Squeeze out points with 0 dof as well */
8029   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8030   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8031     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8032       cpoints[q * 2]     = cpoints[p];
8033       cpoints[q * 2 + 1] = cpoints[p + 1];
8034       ++q;
8035     }
8036   }
8037   numCPoints = q;
8038   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8039     PetscInt fdof;
8040 
8041     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8042     if (!dof) continue;
8043     for (f = 0; f < numFields; ++f) {
8044       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8045       coffsets[f + 1] += fdof;
8046     }
8047     numCIndices += dof;
8048   }
8049   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8050   /* Row indices */
8051   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8052   {
8053     DMPlexTransform tr;
8054     DMPolytopeType *rct;
8055     PetscInt       *rsize, *rcone, *rornt, Nt;
8056 
8057     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8058     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8059     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8060     numSubcells = rsize[Nt - 1];
8061     PetscCall(DMPlexTransformDestroy(&tr));
8062   }
8063   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8064   for (r = 0, q = 0; r < numSubcells; ++r) {
8065     /* TODO Map from coarse to fine cells */
8066     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8067     /* Compress out points not in the section */
8068     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8069     for (p = 0; p < numFPoints * 2; p += 2) {
8070       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8071         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8072         if (!dof) continue;
8073         for (s = 0; s < q; ++s)
8074           if (fpoints[p] == ftotpoints[s * 2]) break;
8075         if (s < q) continue;
8076         ftotpoints[q * 2]     = fpoints[p];
8077         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8078         ++q;
8079       }
8080     }
8081     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8082   }
8083   numFPoints = q;
8084   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8085     PetscInt fdof;
8086 
8087     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8088     if (!dof) continue;
8089     for (f = 0; f < numFields; ++f) {
8090       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8091       foffsets[f + 1] += fdof;
8092     }
8093     numFIndices += dof;
8094   }
8095   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8096 
8097   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8098   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8099   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8100   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8101   if (numFields) {
8102     const PetscInt **permsF[32] = {NULL};
8103     const PetscInt **permsC[32] = {NULL};
8104 
8105     for (f = 0; f < numFields; f++) {
8106       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8107       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8108     }
8109     for (p = 0; p < numFPoints; p++) {
8110       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8111       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8112     }
8113     for (p = 0; p < numCPoints; p++) {
8114       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8115       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8116     }
8117     for (f = 0; f < numFields; f++) {
8118       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8119       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8120     }
8121   } else {
8122     const PetscInt **permsF = NULL;
8123     const PetscInt **permsC = NULL;
8124 
8125     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8126     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8127     for (p = 0, off = 0; p < numFPoints; p++) {
8128       const PetscInt *perm = permsF ? permsF[p] : NULL;
8129 
8130       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8131       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8132     }
8133     for (p = 0, off = 0; p < numCPoints; p++) {
8134       const PetscInt *perm = permsC ? permsC[p] : NULL;
8135 
8136       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8137       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8138     }
8139     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8140     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8141   }
8142   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8143   /* TODO: flips */
8144   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8145   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8146   if (ierr) {
8147     PetscMPIInt rank;
8148 
8149     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8150     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8151     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8152     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8153     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8154   }
8155   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8156   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8157   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8158   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8159   PetscFunctionReturn(PETSC_SUCCESS);
8160 }
8161 
8162 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8163 {
8164   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8165   PetscInt       *cpoints = NULL;
8166   PetscInt        foffsets[32], coffsets[32];
8167   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8168   DMPolytopeType  ct;
8169   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8170 
8171   PetscFunctionBegin;
8172   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8173   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8174   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8175   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8176   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8177   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8178   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8179   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8180   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8181   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8182   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8183   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8184   PetscCall(PetscArrayzero(foffsets, 32));
8185   PetscCall(PetscArrayzero(coffsets, 32));
8186   /* Column indices */
8187   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8188   maxFPoints = numCPoints;
8189   /* Compress out points not in the section */
8190   /*   TODO: Squeeze out points with 0 dof as well */
8191   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8192   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8193     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8194       cpoints[q * 2]     = cpoints[p];
8195       cpoints[q * 2 + 1] = cpoints[p + 1];
8196       ++q;
8197     }
8198   }
8199   numCPoints = q;
8200   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8201     PetscInt fdof;
8202 
8203     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8204     if (!dof) continue;
8205     for (f = 0; f < numFields; ++f) {
8206       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8207       coffsets[f + 1] += fdof;
8208     }
8209     numCIndices += dof;
8210   }
8211   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8212   /* Row indices */
8213   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8214   {
8215     DMPlexTransform tr;
8216     DMPolytopeType *rct;
8217     PetscInt       *rsize, *rcone, *rornt, Nt;
8218 
8219     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8220     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8221     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8222     numSubcells = rsize[Nt - 1];
8223     PetscCall(DMPlexTransformDestroy(&tr));
8224   }
8225   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8226   for (r = 0, q = 0; r < numSubcells; ++r) {
8227     /* TODO Map from coarse to fine cells */
8228     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8229     /* Compress out points not in the section */
8230     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8231     for (p = 0; p < numFPoints * 2; p += 2) {
8232       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8233         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8234         if (!dof) continue;
8235         for (s = 0; s < q; ++s)
8236           if (fpoints[p] == ftotpoints[s * 2]) break;
8237         if (s < q) continue;
8238         ftotpoints[q * 2]     = fpoints[p];
8239         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8240         ++q;
8241       }
8242     }
8243     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8244   }
8245   numFPoints = q;
8246   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8247     PetscInt fdof;
8248 
8249     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8250     if (!dof) continue;
8251     for (f = 0; f < numFields; ++f) {
8252       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8253       foffsets[f + 1] += fdof;
8254     }
8255     numFIndices += dof;
8256   }
8257   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8258 
8259   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8260   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8261   if (numFields) {
8262     const PetscInt **permsF[32] = {NULL};
8263     const PetscInt **permsC[32] = {NULL};
8264 
8265     for (f = 0; f < numFields; f++) {
8266       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8267       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8268     }
8269     for (p = 0; p < numFPoints; p++) {
8270       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8271       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8272     }
8273     for (p = 0; p < numCPoints; p++) {
8274       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8275       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8276     }
8277     for (f = 0; f < numFields; f++) {
8278       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8279       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8280     }
8281   } else {
8282     const PetscInt **permsF = NULL;
8283     const PetscInt **permsC = NULL;
8284 
8285     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8286     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8287     for (p = 0, off = 0; p < numFPoints; p++) {
8288       const PetscInt *perm = permsF ? permsF[p] : NULL;
8289 
8290       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8291       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8292     }
8293     for (p = 0, off = 0; p < numCPoints; p++) {
8294       const PetscInt *perm = permsC ? permsC[p] : NULL;
8295 
8296       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8297       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8298     }
8299     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8300     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8301   }
8302   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8303   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8304   PetscFunctionReturn(PETSC_SUCCESS);
8305 }
8306 
8307 /*@C
8308   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8309 
8310   Input Parameter:
8311 . dm - The `DMPLEX` object
8312 
8313   Output Parameter:
8314 . cellHeight - The height of a cell
8315 
8316   Level: developer
8317 
8318 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8319 @*/
8320 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8321 {
8322   DM_Plex *mesh = (DM_Plex *)dm->data;
8323 
8324   PetscFunctionBegin;
8325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8326   PetscAssertPointer(cellHeight, 2);
8327   *cellHeight = mesh->vtkCellHeight;
8328   PetscFunctionReturn(PETSC_SUCCESS);
8329 }
8330 
8331 /*@C
8332   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8333 
8334   Input Parameters:
8335 + dm         - The `DMPLEX` object
8336 - cellHeight - The height of a cell
8337 
8338   Level: developer
8339 
8340 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8341 @*/
8342 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8343 {
8344   DM_Plex *mesh = (DM_Plex *)dm->data;
8345 
8346   PetscFunctionBegin;
8347   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8348   mesh->vtkCellHeight = cellHeight;
8349   PetscFunctionReturn(PETSC_SUCCESS);
8350 }
8351 
8352 /*@
8353   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8354 
8355   Input Parameters:
8356 + dm - The `DMPLEX` object
8357 - ct - The `DMPolytopeType` of the cell
8358 
8359   Output Parameters:
8360 + start - The first cell of this type, or `NULL`
8361 - end   - The upper bound on this celltype, or `NULL`
8362 
8363   Level: advanced
8364 
8365 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8366 @*/
8367 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8368 {
8369   DM_Plex *mesh = (DM_Plex *)dm->data;
8370   DMLabel  label;
8371   PetscInt pStart, pEnd;
8372 
8373   PetscFunctionBegin;
8374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8375   if (start) {
8376     PetscAssertPointer(start, 3);
8377     *start = 0;
8378   }
8379   if (end) {
8380     PetscAssertPointer(end, 4);
8381     *end = 0;
8382   }
8383   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8384   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8385   if (mesh->tr) {
8386     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8387   } else {
8388     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8389     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8390     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8391   }
8392   PetscFunctionReturn(PETSC_SUCCESS);
8393 }
8394 
8395 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8396 {
8397   PetscSection section, globalSection;
8398   PetscInt    *numbers, p;
8399 
8400   PetscFunctionBegin;
8401   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8402   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8403   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8404   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8405   PetscCall(PetscSectionSetUp(section));
8406   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8407   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8408   for (p = pStart; p < pEnd; ++p) {
8409     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8410     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8411     else numbers[p - pStart] += shift;
8412   }
8413   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8414   if (globalSize) {
8415     PetscLayout layout;
8416     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8417     PetscCall(PetscLayoutGetSize(layout, globalSize));
8418     PetscCall(PetscLayoutDestroy(&layout));
8419   }
8420   PetscCall(PetscSectionDestroy(&section));
8421   PetscCall(PetscSectionDestroy(&globalSection));
8422   PetscFunctionReturn(PETSC_SUCCESS);
8423 }
8424 
8425 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8426 {
8427   PetscInt cellHeight, cStart, cEnd;
8428 
8429   PetscFunctionBegin;
8430   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8431   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8432   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8433   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8434   PetscFunctionReturn(PETSC_SUCCESS);
8435 }
8436 
8437 /*@
8438   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8439 
8440   Input Parameter:
8441 . dm - The `DMPLEX` object
8442 
8443   Output Parameter:
8444 . globalCellNumbers - Global cell numbers for all cells on this process
8445 
8446   Level: developer
8447 
8448 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8449 @*/
8450 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8451 {
8452   DM_Plex *mesh = (DM_Plex *)dm->data;
8453 
8454   PetscFunctionBegin;
8455   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8456   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8457   *globalCellNumbers = mesh->globalCellNumbers;
8458   PetscFunctionReturn(PETSC_SUCCESS);
8459 }
8460 
8461 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8462 {
8463   PetscInt vStart, vEnd;
8464 
8465   PetscFunctionBegin;
8466   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8467   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8468   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8469   PetscFunctionReturn(PETSC_SUCCESS);
8470 }
8471 
8472 /*@
8473   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8474 
8475   Input Parameter:
8476 . dm - The `DMPLEX` object
8477 
8478   Output Parameter:
8479 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8480 
8481   Level: developer
8482 
8483 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8484 @*/
8485 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8486 {
8487   DM_Plex *mesh = (DM_Plex *)dm->data;
8488 
8489   PetscFunctionBegin;
8490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8491   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8492   *globalVertexNumbers = mesh->globalVertexNumbers;
8493   PetscFunctionReturn(PETSC_SUCCESS);
8494 }
8495 
8496 /*@
8497   DMPlexCreatePointNumbering - Create a global numbering for all points.
8498 
8499   Collective
8500 
8501   Input Parameter:
8502 . dm - The `DMPLEX` object
8503 
8504   Output Parameter:
8505 . globalPointNumbers - Global numbers for all points on this process
8506 
8507   Level: developer
8508 
8509   Notes:
8510   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8511   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8512   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8513   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8514 
8515   The partitioned mesh is
8516   ```
8517   (2)--0--(3)--1--(4)    (1)--0--(2)
8518   ```
8519   and its global numbering is
8520   ```
8521   (3)--0--(4)--1--(5)--2--(6)
8522   ```
8523   Then the global numbering is provided as
8524   ```
8525   [0] Number of indices in set 5
8526   [0] 0 0
8527   [0] 1 1
8528   [0] 2 3
8529   [0] 3 4
8530   [0] 4 -6
8531   [1] Number of indices in set 3
8532   [1] 0 2
8533   [1] 1 5
8534   [1] 2 6
8535   ```
8536 
8537 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8538 @*/
8539 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8540 {
8541   IS        nums[4];
8542   PetscInt  depths[4], gdepths[4], starts[4];
8543   PetscInt  depth, d, shift = 0;
8544   PetscBool empty = PETSC_FALSE;
8545 
8546   PetscFunctionBegin;
8547   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8548   PetscCall(DMPlexGetDepth(dm, &depth));
8549   // For unstratified meshes use dim instead of depth
8550   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8551   // If any stratum is empty, we must mark all empty
8552   for (d = 0; d <= depth; ++d) {
8553     PetscInt end;
8554 
8555     depths[d] = depth - d;
8556     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8557     if (!(starts[d] - end)) empty = PETSC_TRUE;
8558   }
8559   if (empty)
8560     for (d = 0; d <= depth; ++d) {
8561       depths[d] = -1;
8562       starts[d] = -1;
8563     }
8564   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8565   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8566   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]);
8567   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8568   for (d = 0; d <= depth; ++d) {
8569     PetscInt pStart, pEnd, gsize;
8570 
8571     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8572     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8573     shift += gsize;
8574   }
8575   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8576   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8577   PetscFunctionReturn(PETSC_SUCCESS);
8578 }
8579 
8580 /*@
8581   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8582 
8583   Input Parameter:
8584 . dm - The `DMPLEX` object
8585 
8586   Output Parameter:
8587 . ranks - The rank field
8588 
8589   Options Database Key:
8590 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8591 
8592   Level: intermediate
8593 
8594 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8595 @*/
8596 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8597 {
8598   DM             rdm;
8599   PetscFE        fe;
8600   PetscScalar   *r;
8601   PetscMPIInt    rank;
8602   DMPolytopeType ct;
8603   PetscInt       dim, cStart, cEnd, c;
8604   PetscBool      simplex;
8605 
8606   PetscFunctionBeginUser;
8607   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8608   PetscAssertPointer(ranks, 2);
8609   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8610   PetscCall(DMClone(dm, &rdm));
8611   PetscCall(DMGetDimension(rdm, &dim));
8612   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8613   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8614   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8615   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8616   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8617   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8618   PetscCall(PetscFEDestroy(&fe));
8619   PetscCall(DMCreateDS(rdm));
8620   PetscCall(DMCreateGlobalVector(rdm, ranks));
8621   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8622   PetscCall(VecGetArray(*ranks, &r));
8623   for (c = cStart; c < cEnd; ++c) {
8624     PetscScalar *lr;
8625 
8626     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8627     if (lr) *lr = rank;
8628   }
8629   PetscCall(VecRestoreArray(*ranks, &r));
8630   PetscCall(DMDestroy(&rdm));
8631   PetscFunctionReturn(PETSC_SUCCESS);
8632 }
8633 
8634 /*@
8635   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8636 
8637   Input Parameters:
8638 + dm    - The `DMPLEX`
8639 - label - The `DMLabel`
8640 
8641   Output Parameter:
8642 . val - The label value field
8643 
8644   Options Database Key:
8645 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8646 
8647   Level: intermediate
8648 
8649 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8650 @*/
8651 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8652 {
8653   DM           rdm;
8654   PetscFE      fe;
8655   PetscScalar *v;
8656   PetscInt     dim, cStart, cEnd, c;
8657 
8658   PetscFunctionBeginUser;
8659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8660   PetscAssertPointer(label, 2);
8661   PetscAssertPointer(val, 3);
8662   PetscCall(DMClone(dm, &rdm));
8663   PetscCall(DMGetDimension(rdm, &dim));
8664   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8665   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8666   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8667   PetscCall(PetscFEDestroy(&fe));
8668   PetscCall(DMCreateDS(rdm));
8669   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8670   PetscCall(DMCreateGlobalVector(rdm, val));
8671   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8672   PetscCall(VecGetArray(*val, &v));
8673   for (c = cStart; c < cEnd; ++c) {
8674     PetscScalar *lv;
8675     PetscInt     cval;
8676 
8677     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8678     PetscCall(DMLabelGetValue(label, c, &cval));
8679     *lv = cval;
8680   }
8681   PetscCall(VecRestoreArray(*val, &v));
8682   PetscCall(DMDestroy(&rdm));
8683   PetscFunctionReturn(PETSC_SUCCESS);
8684 }
8685 
8686 /*@
8687   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8688 
8689   Input Parameter:
8690 . dm - The `DMPLEX` object
8691 
8692   Level: developer
8693 
8694   Notes:
8695   This is a useful diagnostic when creating meshes programmatically.
8696 
8697   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8698 
8699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8700 @*/
8701 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8702 {
8703   PetscSection    coneSection, supportSection;
8704   const PetscInt *cone, *support;
8705   PetscInt        coneSize, c, supportSize, s;
8706   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8707   PetscBool       storagecheck = PETSC_TRUE;
8708 
8709   PetscFunctionBegin;
8710   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8711   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8712   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8713   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8714   /* Check that point p is found in the support of its cone points, and vice versa */
8715   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8716   for (p = pStart; p < pEnd; ++p) {
8717     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8718     PetscCall(DMPlexGetCone(dm, p, &cone));
8719     for (c = 0; c < coneSize; ++c) {
8720       PetscBool dup = PETSC_FALSE;
8721       PetscInt  d;
8722       for (d = c - 1; d >= 0; --d) {
8723         if (cone[c] == cone[d]) {
8724           dup = PETSC_TRUE;
8725           break;
8726         }
8727       }
8728       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8729       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8730       for (s = 0; s < supportSize; ++s) {
8731         if (support[s] == p) break;
8732       }
8733       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8734         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8735         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8736         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8737         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8738         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8739         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8740         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]);
8741         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8742       }
8743     }
8744     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8745     if (p != pp) {
8746       storagecheck = PETSC_FALSE;
8747       continue;
8748     }
8749     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8750     PetscCall(DMPlexGetSupport(dm, p, &support));
8751     for (s = 0; s < supportSize; ++s) {
8752       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8753       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8754       for (c = 0; c < coneSize; ++c) {
8755         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8756         if (cone[c] != pp) {
8757           c = 0;
8758           break;
8759         }
8760         if (cone[c] == p) break;
8761       }
8762       if (c >= coneSize) {
8763         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8764         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8765         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8766         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8767         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8768         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8769         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8770       }
8771     }
8772   }
8773   if (storagecheck) {
8774     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8775     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8776     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8777   }
8778   PetscFunctionReturn(PETSC_SUCCESS);
8779 }
8780 
8781 /*
8782   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.
8783 */
8784 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8785 {
8786   DMPolytopeType  cct;
8787   PetscInt        ptpoints[4];
8788   const PetscInt *cone, *ccone, *ptcone;
8789   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8790 
8791   PetscFunctionBegin;
8792   *unsplit = 0;
8793   switch (ct) {
8794   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8795     ptpoints[npt++] = c;
8796     break;
8797   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8798     PetscCall(DMPlexGetCone(dm, c, &cone));
8799     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8800     for (cp = 0; cp < coneSize; ++cp) {
8801       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8802       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8803     }
8804     break;
8805   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8806   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8807     PetscCall(DMPlexGetCone(dm, c, &cone));
8808     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8809     for (cp = 0; cp < coneSize; ++cp) {
8810       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8811       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8812       for (ccp = 0; ccp < cconeSize; ++ccp) {
8813         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8814         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8815           PetscInt p;
8816           for (p = 0; p < npt; ++p)
8817             if (ptpoints[p] == ccone[ccp]) break;
8818           if (p == npt) ptpoints[npt++] = ccone[ccp];
8819         }
8820       }
8821     }
8822     break;
8823   default:
8824     break;
8825   }
8826   for (pt = 0; pt < npt; ++pt) {
8827     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8828     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8829   }
8830   PetscFunctionReturn(PETSC_SUCCESS);
8831 }
8832 
8833 /*@
8834   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8835 
8836   Input Parameters:
8837 + dm         - The `DMPLEX` object
8838 - cellHeight - Normally 0
8839 
8840   Level: developer
8841 
8842   Notes:
8843   This is a useful diagnostic when creating meshes programmatically.
8844   Currently applicable only to homogeneous simplex or tensor meshes.
8845 
8846   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8847 
8848 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8849 @*/
8850 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8851 {
8852   DMPlexInterpolatedFlag interp;
8853   DMPolytopeType         ct;
8854   PetscInt               vStart, vEnd, cStart, cEnd, c;
8855 
8856   PetscFunctionBegin;
8857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8858   PetscCall(DMPlexIsInterpolated(dm, &interp));
8859   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8860   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8861   for (c = cStart; c < cEnd; ++c) {
8862     PetscInt *closure = NULL;
8863     PetscInt  coneSize, closureSize, cl, Nv = 0;
8864 
8865     PetscCall(DMPlexGetCellType(dm, c, &ct));
8866     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8867     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8868     if (interp == DMPLEX_INTERPOLATED_FULL) {
8869       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8870       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));
8871     }
8872     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8873     for (cl = 0; cl < closureSize * 2; cl += 2) {
8874       const PetscInt p = closure[cl];
8875       if ((p >= vStart) && (p < vEnd)) ++Nv;
8876     }
8877     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8878     /* Special Case: Tensor faces with identified vertices */
8879     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8880       PetscInt unsplit;
8881 
8882       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8883       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8884     }
8885     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));
8886   }
8887   PetscFunctionReturn(PETSC_SUCCESS);
8888 }
8889 
8890 /*@
8891   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8892 
8893   Collective
8894 
8895   Input Parameters:
8896 + dm         - The `DMPLEX` object
8897 - cellHeight - Normally 0
8898 
8899   Level: developer
8900 
8901   Notes:
8902   This is a useful diagnostic when creating meshes programmatically.
8903   This routine is only relevant for meshes that are fully interpolated across all ranks.
8904   It will error out if a partially interpolated mesh is given on some rank.
8905   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8906 
8907   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8908 
8909 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8910 @*/
8911 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8912 {
8913   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8914   DMPlexInterpolatedFlag interpEnum;
8915 
8916   PetscFunctionBegin;
8917   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8918   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8919   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8920   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8921     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8922     PetscFunctionReturn(PETSC_SUCCESS);
8923   }
8924 
8925   PetscCall(DMGetDimension(dm, &dim));
8926   PetscCall(DMPlexGetDepth(dm, &depth));
8927   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8928   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8929     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8930     for (c = cStart; c < cEnd; ++c) {
8931       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8932       const DMPolytopeType *faceTypes;
8933       DMPolytopeType        ct;
8934       PetscInt              numFaces, coneSize, f;
8935       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8936 
8937       PetscCall(DMPlexGetCellType(dm, c, &ct));
8938       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8939       if (unsplit) continue;
8940       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8941       PetscCall(DMPlexGetCone(dm, c, &cone));
8942       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8943       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8944       for (cl = 0; cl < closureSize * 2; cl += 2) {
8945         const PetscInt p = closure[cl];
8946         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8947       }
8948       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8949       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);
8950       for (f = 0; f < numFaces; ++f) {
8951         DMPolytopeType fct;
8952         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8953 
8954         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8955         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8956         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8957           const PetscInt p = fclosure[cl];
8958           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8959         }
8960         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]);
8961         for (v = 0; v < fnumCorners; ++v) {
8962           if (fclosure[v] != faces[fOff + v]) {
8963             PetscInt v1;
8964 
8965             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8966             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8967             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8968             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8969             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8970             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]);
8971           }
8972         }
8973         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8974         fOff += faceSizes[f];
8975       }
8976       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8977       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8978     }
8979   }
8980   PetscFunctionReturn(PETSC_SUCCESS);
8981 }
8982 
8983 /*@
8984   DMPlexCheckGeometry - Check the geometry of mesh cells
8985 
8986   Input Parameter:
8987 . dm - The `DMPLEX` object
8988 
8989   Level: developer
8990 
8991   Notes:
8992   This is a useful diagnostic when creating meshes programmatically.
8993 
8994   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8995 
8996 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8997 @*/
8998 PetscErrorCode DMPlexCheckGeometry(DM dm)
8999 {
9000   Vec       coordinates;
9001   PetscReal detJ, J[9], refVol = 1.0;
9002   PetscReal vol;
9003   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9004 
9005   PetscFunctionBegin;
9006   PetscCall(DMGetDimension(dm, &dim));
9007   PetscCall(DMGetCoordinateDim(dm, &dE));
9008   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9009   PetscCall(DMPlexGetDepth(dm, &depth));
9010   for (d = 0; d < dim; ++d) refVol *= 2.0;
9011   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9012   /* Make sure local coordinates are created, because that step is collective */
9013   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9014   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9015   for (c = cStart; c < cEnd; ++c) {
9016     DMPolytopeType ct;
9017     PetscInt       unsplit;
9018     PetscBool      ignoreZeroVol = PETSC_FALSE;
9019 
9020     PetscCall(DMPlexGetCellType(dm, c, &ct));
9021     switch (ct) {
9022     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9023     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9024     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9025       ignoreZeroVol = PETSC_TRUE;
9026       break;
9027     default:
9028       break;
9029     }
9030     switch (ct) {
9031     case DM_POLYTOPE_TRI_PRISM:
9032     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9033     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9034     case DM_POLYTOPE_PYRAMID:
9035       continue;
9036     default:
9037       break;
9038     }
9039     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9040     if (unsplit) continue;
9041     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9042     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);
9043     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9044     /* This should work with periodicity since DG coordinates should be used */
9045     if (depth > 1) {
9046       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9047       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);
9048       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9049     }
9050   }
9051   PetscFunctionReturn(PETSC_SUCCESS);
9052 }
9053 
9054 /*@
9055   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9056 
9057   Collective
9058 
9059   Input Parameters:
9060 + dm              - The `DMPLEX` object
9061 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9062 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9063 
9064   Level: developer
9065 
9066   Notes:
9067   This is mainly intended for debugging/testing purposes.
9068 
9069   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9070 
9071   Extra roots can come from priodic cuts, where additional points appear on the boundary
9072 
9073 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9074 @*/
9075 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9076 {
9077   PetscInt           l, nleaves, nroots, overlap;
9078   const PetscInt    *locals;
9079   const PetscSFNode *remotes;
9080   PetscBool          distributed;
9081   MPI_Comm           comm;
9082   PetscMPIInt        rank;
9083 
9084   PetscFunctionBegin;
9085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9086   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9087   else pointSF = dm->sf;
9088   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9089   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9090   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9091   {
9092     PetscMPIInt mpiFlag;
9093 
9094     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9095     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9096   }
9097   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9098   PetscCall(DMPlexIsDistributed(dm, &distributed));
9099   if (!distributed) {
9100     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);
9101     PetscFunctionReturn(PETSC_SUCCESS);
9102   }
9103   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);
9104   PetscCall(DMPlexGetOverlap(dm, &overlap));
9105 
9106   /* Check SF graph is compatible with DMPlex chart */
9107   {
9108     PetscInt pStart, pEnd, maxLeaf;
9109 
9110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9111     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9112     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9113     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9114   }
9115 
9116   /* Check Point SF has no local points referenced */
9117   for (l = 0; l < nleaves; l++) {
9118     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);
9119   }
9120 
9121   /* Check there are no cells in interface */
9122   if (!overlap) {
9123     PetscInt cellHeight, cStart, cEnd;
9124 
9125     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9126     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9127     for (l = 0; l < nleaves; ++l) {
9128       const PetscInt point = locals ? locals[l] : l;
9129 
9130       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9131     }
9132   }
9133 
9134   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9135   {
9136     const PetscInt *rootdegree;
9137 
9138     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9139     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9140     for (l = 0; l < nleaves; ++l) {
9141       const PetscInt  point = locals ? locals[l] : l;
9142       const PetscInt *cone;
9143       PetscInt        coneSize, c, idx;
9144 
9145       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9146       PetscCall(DMPlexGetCone(dm, point, &cone));
9147       for (c = 0; c < coneSize; ++c) {
9148         if (!rootdegree[cone[c]]) {
9149           if (locals) {
9150             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9151           } else {
9152             idx = (cone[c] < nleaves) ? cone[c] : -1;
9153           }
9154           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9155         }
9156       }
9157     }
9158   }
9159   PetscFunctionReturn(PETSC_SUCCESS);
9160 }
9161 
9162 /*@
9163   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9164 
9165   Input Parameter:
9166 . dm - The `DMPLEX` object
9167 
9168   Level: developer
9169 
9170   Notes:
9171   This is a useful diagnostic when creating meshes programmatically.
9172 
9173   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9174 
9175   Currently does not include `DMPlexCheckCellShape()`.
9176 
9177 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9178 @*/
9179 PetscErrorCode DMPlexCheck(DM dm)
9180 {
9181   PetscInt cellHeight;
9182 
9183   PetscFunctionBegin;
9184   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9185   PetscCall(DMPlexCheckSymmetry(dm));
9186   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9187   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9188   PetscCall(DMPlexCheckGeometry(dm));
9189   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9190   PetscCall(DMPlexCheckInterfaceCones(dm));
9191   PetscFunctionReturn(PETSC_SUCCESS);
9192 }
9193 
9194 typedef struct cell_stats {
9195   PetscReal min, max, sum, squaresum;
9196   PetscInt  count;
9197 } cell_stats_t;
9198 
9199 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9200 {
9201   PetscInt i, N = *len;
9202 
9203   for (i = 0; i < N; i++) {
9204     cell_stats_t *A = (cell_stats_t *)a;
9205     cell_stats_t *B = (cell_stats_t *)b;
9206 
9207     B->min = PetscMin(A->min, B->min);
9208     B->max = PetscMax(A->max, B->max);
9209     B->sum += A->sum;
9210     B->squaresum += A->squaresum;
9211     B->count += A->count;
9212   }
9213 }
9214 
9215 /*@
9216   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9217 
9218   Collective
9219 
9220   Input Parameters:
9221 + dm        - The `DMPLEX` object
9222 . output    - If true, statistics will be displayed on `stdout`
9223 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9224 
9225   Level: developer
9226 
9227   Notes:
9228   This is mainly intended for debugging/testing purposes.
9229 
9230   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9231 
9232 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9233 @*/
9234 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9235 {
9236   DM           dmCoarse;
9237   cell_stats_t stats, globalStats;
9238   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9239   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9240   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9241   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9242   PetscMPIInt  rank, size;
9243 
9244   PetscFunctionBegin;
9245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9246   stats.min = PETSC_MAX_REAL;
9247   stats.max = PETSC_MIN_REAL;
9248   stats.sum = stats.squaresum = 0.;
9249   stats.count                 = 0;
9250 
9251   PetscCallMPI(MPI_Comm_size(comm, &size));
9252   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9253   PetscCall(DMGetCoordinateDim(dm, &cdim));
9254   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9255   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9256   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9257   for (c = cStart; c < cEnd; c++) {
9258     PetscInt  i;
9259     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9260 
9261     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9262     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9263     for (i = 0; i < PetscSqr(cdim); ++i) {
9264       frobJ += J[i] * J[i];
9265       frobInvJ += invJ[i] * invJ[i];
9266     }
9267     cond2 = frobJ * frobInvJ;
9268     cond  = PetscSqrtReal(cond2);
9269 
9270     stats.min = PetscMin(stats.min, cond);
9271     stats.max = PetscMax(stats.max, cond);
9272     stats.sum += cond;
9273     stats.squaresum += cond2;
9274     stats.count++;
9275     if (output && cond > limit) {
9276       PetscSection coordSection;
9277       Vec          coordsLocal;
9278       PetscScalar *coords = NULL;
9279       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9280 
9281       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9282       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9283       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9284       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9285       for (i = 0; i < Nv / cdim; ++i) {
9286         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9287         for (d = 0; d < cdim; ++d) {
9288           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9289           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9290         }
9291         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9292       }
9293       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9294       for (cl = 0; cl < clSize * 2; cl += 2) {
9295         const PetscInt edge = closure[cl];
9296 
9297         if ((edge >= eStart) && (edge < eEnd)) {
9298           PetscReal len;
9299 
9300           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9301           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9302         }
9303       }
9304       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9305       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9306     }
9307   }
9308   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9309 
9310   if (size > 1) {
9311     PetscMPIInt  blockLengths[2] = {4, 1};
9312     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9313     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9314     MPI_Op       statReduce;
9315 
9316     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9317     PetscCallMPI(MPI_Type_commit(&statType));
9318     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9319     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9320     PetscCallMPI(MPI_Op_free(&statReduce));
9321     PetscCallMPI(MPI_Type_free(&statType));
9322   } else {
9323     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9324   }
9325   if (rank == 0) {
9326     count = globalStats.count;
9327     min   = globalStats.min;
9328     max   = globalStats.max;
9329     mean  = globalStats.sum / globalStats.count;
9330     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9331   }
9332 
9333   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));
9334   PetscCall(PetscFree2(J, invJ));
9335 
9336   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9337   if (dmCoarse) {
9338     PetscBool isplex;
9339 
9340     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9341     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9342   }
9343   PetscFunctionReturn(PETSC_SUCCESS);
9344 }
9345 
9346 /*@
9347   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9348   orthogonal quality below given tolerance.
9349 
9350   Collective
9351 
9352   Input Parameters:
9353 + dm   - The `DMPLEX` object
9354 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9355 - atol - [0, 1] Absolute tolerance for tagging cells.
9356 
9357   Output Parameters:
9358 + OrthQual      - `Vec` containing orthogonal quality per cell
9359 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9360 
9361   Options Database Keys:
9362 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9363 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9364 
9365   Level: intermediate
9366 
9367   Notes:
9368   Orthogonal quality is given by the following formula:
9369 
9370   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9371 
9372   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
9373   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9374   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9375   calculating the cosine of the angle between these vectors.
9376 
9377   Orthogonal quality ranges from 1 (best) to 0 (worst).
9378 
9379   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9380   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9381 
9382   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9383 
9384 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9385 @*/
9386 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9387 {
9388   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9389   PetscInt              *idx;
9390   PetscScalar           *oqVals;
9391   const PetscScalar     *cellGeomArr, *faceGeomArr;
9392   PetscReal             *ci, *fi, *Ai;
9393   MPI_Comm               comm;
9394   Vec                    cellgeom, facegeom;
9395   DM                     dmFace, dmCell;
9396   IS                     glob;
9397   ISLocalToGlobalMapping ltog;
9398   PetscViewer            vwr;
9399 
9400   PetscFunctionBegin;
9401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9402   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9403   PetscAssertPointer(OrthQual, 4);
9404   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9405   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9406   PetscCall(DMGetDimension(dm, &nc));
9407   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9408   {
9409     DMPlexInterpolatedFlag interpFlag;
9410 
9411     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9412     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9413       PetscMPIInt rank;
9414 
9415       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9416       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9417     }
9418   }
9419   if (OrthQualLabel) {
9420     PetscAssertPointer(OrthQualLabel, 5);
9421     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9422     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9423   } else {
9424     *OrthQualLabel = NULL;
9425   }
9426   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9427   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9428   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9429   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9430   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9431   PetscCall(VecCreate(comm, OrthQual));
9432   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9433   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9434   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9435   PetscCall(VecSetUp(*OrthQual));
9436   PetscCall(ISDestroy(&glob));
9437   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9438   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9439   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9440   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9441   PetscCall(VecGetDM(cellgeom, &dmCell));
9442   PetscCall(VecGetDM(facegeom, &dmFace));
9443   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9444   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9445     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9446     PetscInt         cellarr[2], *adj = NULL;
9447     PetscScalar     *cArr, *fArr;
9448     PetscReal        minvalc = 1.0, minvalf = 1.0;
9449     PetscFVCellGeom *cg;
9450 
9451     idx[cellIter] = cell - cStart;
9452     cellarr[0]    = cell;
9453     /* Make indexing into cellGeom easier */
9454     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9455     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9456     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9457     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9458     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9459       PetscInt         i;
9460       const PetscInt   neigh  = adj[cellneigh];
9461       PetscReal        normci = 0, normfi = 0, normai = 0;
9462       PetscFVCellGeom *cgneigh;
9463       PetscFVFaceGeom *fg;
9464 
9465       /* Don't count ourselves in the neighbor list */
9466       if (neigh == cell) continue;
9467       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9468       cellarr[1] = neigh;
9469       {
9470         PetscInt        numcovpts;
9471         const PetscInt *covpts;
9472 
9473         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9474         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9475         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9476       }
9477 
9478       /* Compute c_i, f_i and their norms */
9479       for (i = 0; i < nc; i++) {
9480         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9481         fi[i] = fg->centroid[i] - cg->centroid[i];
9482         Ai[i] = fg->normal[i];
9483         normci += PetscPowReal(ci[i], 2);
9484         normfi += PetscPowReal(fi[i], 2);
9485         normai += PetscPowReal(Ai[i], 2);
9486       }
9487       normci = PetscSqrtReal(normci);
9488       normfi = PetscSqrtReal(normfi);
9489       normai = PetscSqrtReal(normai);
9490 
9491       /* Normalize and compute for each face-cell-normal pair */
9492       for (i = 0; i < nc; i++) {
9493         ci[i] = ci[i] / normci;
9494         fi[i] = fi[i] / normfi;
9495         Ai[i] = Ai[i] / normai;
9496         /* PetscAbs because I don't know if normals are guaranteed to point out */
9497         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9498         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9499       }
9500       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9501       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9502     }
9503     PetscCall(PetscFree(adj));
9504     PetscCall(PetscFree2(cArr, fArr));
9505     /* Defer to cell if they're equal */
9506     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9507     if (OrthQualLabel) {
9508       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9509     }
9510   }
9511   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9512   PetscCall(VecAssemblyBegin(*OrthQual));
9513   PetscCall(VecAssemblyEnd(*OrthQual));
9514   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9515   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9516   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9517   if (OrthQualLabel) {
9518     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9519   }
9520   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9521   PetscCall(PetscViewerDestroy(&vwr));
9522   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9523   PetscFunctionReturn(PETSC_SUCCESS);
9524 }
9525 
9526 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9527  * interpolator construction */
9528 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9529 {
9530   PetscSection section, newSection, gsection;
9531   PetscSF      sf;
9532   PetscBool    hasConstraints, ghasConstraints;
9533 
9534   PetscFunctionBegin;
9535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9536   PetscAssertPointer(odm, 2);
9537   PetscCall(DMGetLocalSection(dm, &section));
9538   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9539   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9540   if (!ghasConstraints) {
9541     PetscCall(PetscObjectReference((PetscObject)dm));
9542     *odm = dm;
9543     PetscFunctionReturn(PETSC_SUCCESS);
9544   }
9545   PetscCall(DMClone(dm, odm));
9546   PetscCall(DMCopyFields(dm, *odm));
9547   PetscCall(DMGetLocalSection(*odm, &newSection));
9548   PetscCall(DMGetPointSF(*odm, &sf));
9549   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9550   PetscCall(DMSetGlobalSection(*odm, gsection));
9551   PetscCall(PetscSectionDestroy(&gsection));
9552   PetscFunctionReturn(PETSC_SUCCESS);
9553 }
9554 
9555 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9556 {
9557   DM        dmco, dmfo;
9558   Mat       interpo;
9559   Vec       rscale;
9560   Vec       cglobalo, clocal;
9561   Vec       fglobal, fglobalo, flocal;
9562   PetscBool regular;
9563 
9564   PetscFunctionBegin;
9565   PetscCall(DMGetFullDM(dmc, &dmco));
9566   PetscCall(DMGetFullDM(dmf, &dmfo));
9567   PetscCall(DMSetCoarseDM(dmfo, dmco));
9568   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9569   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9570   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9571   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9572   PetscCall(DMCreateLocalVector(dmc, &clocal));
9573   PetscCall(VecSet(cglobalo, 0.));
9574   PetscCall(VecSet(clocal, 0.));
9575   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9576   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9577   PetscCall(DMCreateLocalVector(dmf, &flocal));
9578   PetscCall(VecSet(fglobal, 0.));
9579   PetscCall(VecSet(fglobalo, 0.));
9580   PetscCall(VecSet(flocal, 0.));
9581   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9582   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9583   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9584   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9585   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9586   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9587   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9588   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9589   *shift = fglobal;
9590   PetscCall(VecDestroy(&flocal));
9591   PetscCall(VecDestroy(&fglobalo));
9592   PetscCall(VecDestroy(&clocal));
9593   PetscCall(VecDestroy(&cglobalo));
9594   PetscCall(VecDestroy(&rscale));
9595   PetscCall(MatDestroy(&interpo));
9596   PetscCall(DMDestroy(&dmfo));
9597   PetscCall(DMDestroy(&dmco));
9598   PetscFunctionReturn(PETSC_SUCCESS);
9599 }
9600 
9601 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9602 {
9603   PetscObject shifto;
9604   Vec         shift;
9605 
9606   PetscFunctionBegin;
9607   if (!interp) {
9608     Vec rscale;
9609 
9610     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9611     PetscCall(VecDestroy(&rscale));
9612   } else {
9613     PetscCall(PetscObjectReference((PetscObject)interp));
9614   }
9615   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9616   if (!shifto) {
9617     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9618     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9619     shifto = (PetscObject)shift;
9620     PetscCall(VecDestroy(&shift));
9621   }
9622   shift = (Vec)shifto;
9623   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9624   PetscCall(VecAXPY(fineSol, 1.0, shift));
9625   PetscCall(MatDestroy(&interp));
9626   PetscFunctionReturn(PETSC_SUCCESS);
9627 }
9628 
9629 /* Pointwise interpolation
9630      Just code FEM for now
9631      u^f = I u^c
9632      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9633      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9634      I_{ij} = psi^f_i phi^c_j
9635 */
9636 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9637 {
9638   PetscSection gsc, gsf;
9639   PetscInt     m, n;
9640   void        *ctx;
9641   DM           cdm;
9642   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9643 
9644   PetscFunctionBegin;
9645   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9646   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9647   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9648   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9649 
9650   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9651   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9652   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9653   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9654   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9655 
9656   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9657   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9658   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9659   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9660   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9661   if (scaling) {
9662     /* Use naive scaling */
9663     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9664   }
9665   PetscFunctionReturn(PETSC_SUCCESS);
9666 }
9667 
9668 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9669 {
9670   VecScatter ctx;
9671 
9672   PetscFunctionBegin;
9673   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9674   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9675   PetscCall(VecScatterDestroy(&ctx));
9676   PetscFunctionReturn(PETSC_SUCCESS);
9677 }
9678 
9679 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[])
9680 {
9681   const PetscInt Nc = uOff[1] - uOff[0];
9682   PetscInt       c;
9683   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9684 }
9685 
9686 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9687 {
9688   DM           dmc;
9689   PetscDS      ds;
9690   Vec          ones, locmass;
9691   IS           cellIS;
9692   PetscFormKey key;
9693   PetscInt     depth;
9694 
9695   PetscFunctionBegin;
9696   PetscCall(DMClone(dm, &dmc));
9697   PetscCall(DMCopyDisc(dm, dmc));
9698   PetscCall(DMGetDS(dmc, &ds));
9699   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9700   PetscCall(DMCreateGlobalVector(dmc, mass));
9701   PetscCall(DMGetLocalVector(dmc, &ones));
9702   PetscCall(DMGetLocalVector(dmc, &locmass));
9703   PetscCall(DMPlexGetDepth(dmc, &depth));
9704   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9705   PetscCall(VecSet(locmass, 0.0));
9706   PetscCall(VecSet(ones, 1.0));
9707   key.label = NULL;
9708   key.value = 0;
9709   key.field = 0;
9710   key.part  = 0;
9711   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9712   PetscCall(ISDestroy(&cellIS));
9713   PetscCall(VecSet(*mass, 0.0));
9714   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9715   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9716   PetscCall(DMRestoreLocalVector(dmc, &ones));
9717   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9718   PetscCall(DMDestroy(&dmc));
9719   PetscFunctionReturn(PETSC_SUCCESS);
9720 }
9721 
9722 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9723 {
9724   PetscSection gsc, gsf;
9725   PetscInt     m, n;
9726   void        *ctx;
9727   DM           cdm;
9728   PetscBool    regular;
9729 
9730   PetscFunctionBegin;
9731   if (dmFine == dmCoarse) {
9732     DM            dmc;
9733     PetscDS       ds;
9734     PetscWeakForm wf;
9735     Vec           u;
9736     IS            cellIS;
9737     PetscFormKey  key;
9738     PetscInt      depth;
9739 
9740     PetscCall(DMClone(dmFine, &dmc));
9741     PetscCall(DMCopyDisc(dmFine, dmc));
9742     PetscCall(DMGetDS(dmc, &ds));
9743     PetscCall(PetscDSGetWeakForm(ds, &wf));
9744     PetscCall(PetscWeakFormClear(wf));
9745     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9746     PetscCall(DMCreateMatrix(dmc, mass));
9747     PetscCall(DMGetLocalVector(dmc, &u));
9748     PetscCall(DMPlexGetDepth(dmc, &depth));
9749     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9750     PetscCall(MatZeroEntries(*mass));
9751     key.label = NULL;
9752     key.value = 0;
9753     key.field = 0;
9754     key.part  = 0;
9755     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9756     PetscCall(ISDestroy(&cellIS));
9757     PetscCall(DMRestoreLocalVector(dmc, &u));
9758     PetscCall(DMDestroy(&dmc));
9759   } else {
9760     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9761     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9762     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9763     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9764 
9765     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9766     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9767     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9768     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9769 
9770     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9771     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9772     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9773     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9774   }
9775   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9776   PetscFunctionReturn(PETSC_SUCCESS);
9777 }
9778 
9779 /*@
9780   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9781 
9782   Input Parameter:
9783 . dm - The `DMPLEX` object
9784 
9785   Output Parameter:
9786 . regular - The flag
9787 
9788   Level: intermediate
9789 
9790 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9791 @*/
9792 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9793 {
9794   PetscFunctionBegin;
9795   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9796   PetscAssertPointer(regular, 2);
9797   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9798   PetscFunctionReturn(PETSC_SUCCESS);
9799 }
9800 
9801 /*@
9802   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9803 
9804   Input Parameters:
9805 + dm      - The `DMPLEX` object
9806 - regular - The flag
9807 
9808   Level: intermediate
9809 
9810 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9811 @*/
9812 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9813 {
9814   PetscFunctionBegin;
9815   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9816   ((DM_Plex *)dm->data)->regularRefinement = regular;
9817   PetscFunctionReturn(PETSC_SUCCESS);
9818 }
9819 
9820 /*@
9821   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9822   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9823 
9824   Not Collective
9825 
9826   Input Parameter:
9827 . dm - The `DMPLEX` object
9828 
9829   Output Parameters:
9830 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9831 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9832 
9833   Level: intermediate
9834 
9835 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9836 @*/
9837 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9838 {
9839   DM_Plex *plex = (DM_Plex *)dm->data;
9840 
9841   PetscFunctionBegin;
9842   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9843   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9844   if (anchorSection) *anchorSection = plex->anchorSection;
9845   if (anchorIS) *anchorIS = plex->anchorIS;
9846   PetscFunctionReturn(PETSC_SUCCESS);
9847 }
9848 
9849 /*@
9850   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9851   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9852   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9853 
9854   Collective
9855 
9856   Input Parameters:
9857 + dm            - The `DMPLEX` object
9858 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9859                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9860 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9861 
9862   Level: intermediate
9863 
9864   Notes:
9865   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9866   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9867 
9868   The reference counts of `anchorSection` and `anchorIS` are incremented.
9869 
9870 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9871 @*/
9872 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9873 {
9874   DM_Plex    *plex = (DM_Plex *)dm->data;
9875   PetscMPIInt result;
9876 
9877   PetscFunctionBegin;
9878   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9879   if (anchorSection) {
9880     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9881     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9882     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9883   }
9884   if (anchorIS) {
9885     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9886     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9887     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9888   }
9889 
9890   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9891   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9892   plex->anchorSection = anchorSection;
9893 
9894   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9895   PetscCall(ISDestroy(&plex->anchorIS));
9896   plex->anchorIS = anchorIS;
9897 
9898   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9899     PetscInt        size, a, pStart, pEnd;
9900     const PetscInt *anchors;
9901 
9902     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9903     PetscCall(ISGetLocalSize(anchorIS, &size));
9904     PetscCall(ISGetIndices(anchorIS, &anchors));
9905     for (a = 0; a < size; a++) {
9906       PetscInt p;
9907 
9908       p = anchors[a];
9909       if (p >= pStart && p < pEnd) {
9910         PetscInt dof;
9911 
9912         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9913         if (dof) {
9914           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9915           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9916         }
9917       }
9918     }
9919     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9920   }
9921   /* reset the generic constraints */
9922   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9923   PetscFunctionReturn(PETSC_SUCCESS);
9924 }
9925 
9926 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9927 {
9928   PetscSection anchorSection;
9929   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9930 
9931   PetscFunctionBegin;
9932   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9933   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9934   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9935   PetscCall(PetscSectionGetNumFields(section, &numFields));
9936   if (numFields) {
9937     PetscInt f;
9938     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9939 
9940     for (f = 0; f < numFields; f++) {
9941       PetscInt numComp;
9942 
9943       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9944       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9945     }
9946   }
9947   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9948   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9949   pStart = PetscMax(pStart, sStart);
9950   pEnd   = PetscMin(pEnd, sEnd);
9951   pEnd   = PetscMax(pStart, pEnd);
9952   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9953   for (p = pStart; p < pEnd; p++) {
9954     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9955     if (dof) {
9956       PetscCall(PetscSectionGetDof(section, p, &dof));
9957       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9958       for (f = 0; f < numFields; f++) {
9959         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9960         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9961       }
9962     }
9963   }
9964   PetscCall(PetscSectionSetUp(*cSec));
9965   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9966   PetscFunctionReturn(PETSC_SUCCESS);
9967 }
9968 
9969 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9970 {
9971   PetscSection    aSec;
9972   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9973   const PetscInt *anchors;
9974   PetscInt        numFields, f;
9975   IS              aIS;
9976   MatType         mtype;
9977   PetscBool       iscuda, iskokkos;
9978 
9979   PetscFunctionBegin;
9980   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9981   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9982   PetscCall(PetscSectionGetStorageSize(section, &n));
9983   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9984   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9985   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9986   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9987   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9988   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9989   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9990   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9991   else mtype = MATSEQAIJ;
9992   PetscCall(MatSetType(*cMat, mtype));
9993   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9994   PetscCall(ISGetIndices(aIS, &anchors));
9995   /* cSec will be a subset of aSec and section */
9996   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9997   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9998   PetscCall(PetscMalloc1(m + 1, &i));
9999   i[0] = 0;
10000   PetscCall(PetscSectionGetNumFields(section, &numFields));
10001   for (p = pStart; p < pEnd; p++) {
10002     PetscInt rDof, rOff, r;
10003 
10004     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10005     if (!rDof) continue;
10006     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10007     if (numFields) {
10008       for (f = 0; f < numFields; f++) {
10009         annz = 0;
10010         for (r = 0; r < rDof; r++) {
10011           a = anchors[rOff + r];
10012           if (a < sStart || a >= sEnd) continue;
10013           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10014           annz += aDof;
10015         }
10016         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10017         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10018         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10019       }
10020     } else {
10021       annz = 0;
10022       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10023       for (q = 0; q < dof; q++) {
10024         a = anchors[rOff + q];
10025         if (a < sStart || a >= sEnd) continue;
10026         PetscCall(PetscSectionGetDof(section, a, &aDof));
10027         annz += aDof;
10028       }
10029       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10030       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10031       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10032     }
10033   }
10034   nnz = i[m];
10035   PetscCall(PetscMalloc1(nnz, &j));
10036   offset = 0;
10037   for (p = pStart; p < pEnd; p++) {
10038     if (numFields) {
10039       for (f = 0; f < numFields; f++) {
10040         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10041         for (q = 0; q < dof; q++) {
10042           PetscInt rDof, rOff, r;
10043           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10044           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10045           for (r = 0; r < rDof; r++) {
10046             PetscInt s;
10047 
10048             a = anchors[rOff + r];
10049             if (a < sStart || a >= sEnd) continue;
10050             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10051             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10052             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10053           }
10054         }
10055       }
10056     } else {
10057       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10058       for (q = 0; q < dof; q++) {
10059         PetscInt rDof, rOff, r;
10060         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10061         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10062         for (r = 0; r < rDof; r++) {
10063           PetscInt s;
10064 
10065           a = anchors[rOff + r];
10066           if (a < sStart || a >= sEnd) continue;
10067           PetscCall(PetscSectionGetDof(section, a, &aDof));
10068           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10069           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10070         }
10071       }
10072     }
10073   }
10074   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10075   PetscCall(PetscFree(i));
10076   PetscCall(PetscFree(j));
10077   PetscCall(ISRestoreIndices(aIS, &anchors));
10078   PetscFunctionReturn(PETSC_SUCCESS);
10079 }
10080 
10081 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10082 {
10083   DM_Plex     *plex = (DM_Plex *)dm->data;
10084   PetscSection anchorSection, section, cSec;
10085   Mat          cMat;
10086 
10087   PetscFunctionBegin;
10088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10089   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10090   if (anchorSection) {
10091     PetscInt Nf;
10092 
10093     PetscCall(DMGetLocalSection(dm, &section));
10094     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10095     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10096     PetscCall(DMGetNumFields(dm, &Nf));
10097     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10098     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10099     PetscCall(PetscSectionDestroy(&cSec));
10100     PetscCall(MatDestroy(&cMat));
10101   }
10102   PetscFunctionReturn(PETSC_SUCCESS);
10103 }
10104 
10105 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10106 {
10107   IS           subis;
10108   PetscSection section, subsection;
10109 
10110   PetscFunctionBegin;
10111   PetscCall(DMGetLocalSection(dm, &section));
10112   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10113   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10114   /* Create subdomain */
10115   PetscCall(DMPlexFilter(dm, label, value, subdm));
10116   /* Create submodel */
10117   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10118   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10119   PetscCall(DMSetLocalSection(*subdm, subsection));
10120   PetscCall(PetscSectionDestroy(&subsection));
10121   PetscCall(DMCopyDisc(dm, *subdm));
10122   /* Create map from submodel to global model */
10123   if (is) {
10124     PetscSection    sectionGlobal, subsectionGlobal;
10125     IS              spIS;
10126     const PetscInt *spmap;
10127     PetscInt       *subIndices;
10128     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10129     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10130 
10131     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10132     PetscCall(ISGetIndices(spIS, &spmap));
10133     PetscCall(PetscSectionGetNumFields(section, &Nf));
10134     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10135     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10136     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10137     for (p = pStart; p < pEnd; ++p) {
10138       PetscInt gdof, pSubSize = 0;
10139 
10140       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10141       if (gdof > 0) {
10142         for (f = 0; f < Nf; ++f) {
10143           PetscInt fdof, fcdof;
10144 
10145           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10146           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10147           pSubSize += fdof - fcdof;
10148         }
10149         subSize += pSubSize;
10150         if (pSubSize) {
10151           if (bs < 0) {
10152             bs = pSubSize;
10153           } else if (bs != pSubSize) {
10154             /* Layout does not admit a pointwise block size */
10155             bs = 1;
10156           }
10157         }
10158       }
10159     }
10160     /* Must have same blocksize on all procs (some might have no points) */
10161     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10162     bsLocal[1] = bs;
10163     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10164     if (bsMinMax[0] != bsMinMax[1]) {
10165       bs = 1;
10166     } else {
10167       bs = bsMinMax[0];
10168     }
10169     PetscCall(PetscMalloc1(subSize, &subIndices));
10170     for (p = pStart; p < pEnd; ++p) {
10171       PetscInt gdof, goff;
10172 
10173       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10174       if (gdof > 0) {
10175         const PetscInt point = spmap[p];
10176 
10177         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10178         for (f = 0; f < Nf; ++f) {
10179           PetscInt fdof, fcdof, fc, f2, poff = 0;
10180 
10181           /* Can get rid of this loop by storing field information in the global section */
10182           for (f2 = 0; f2 < f; ++f2) {
10183             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10184             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10185             poff += fdof - fcdof;
10186           }
10187           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10188           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10189           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10190         }
10191       }
10192     }
10193     PetscCall(ISRestoreIndices(spIS, &spmap));
10194     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10195     if (bs > 1) {
10196       /* We need to check that the block size does not come from non-contiguous fields */
10197       PetscInt i, j, set = 1;
10198       for (i = 0; i < subSize; i += bs) {
10199         for (j = 0; j < bs; ++j) {
10200           if (subIndices[i + j] != subIndices[i] + j) {
10201             set = 0;
10202             break;
10203           }
10204         }
10205       }
10206       if (set) PetscCall(ISSetBlockSize(*is, bs));
10207     }
10208     /* Attach nullspace */
10209     for (f = 0; f < Nf; ++f) {
10210       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10211       if ((*subdm)->nullspaceConstructors[f]) break;
10212     }
10213     if (f < Nf) {
10214       MatNullSpace nullSpace;
10215       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10216 
10217       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10218       PetscCall(MatNullSpaceDestroy(&nullSpace));
10219     }
10220   }
10221   PetscFunctionReturn(PETSC_SUCCESS);
10222 }
10223 
10224 /*@
10225   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10226 
10227   Input Parameters:
10228 + dm    - The `DM`
10229 - dummy - unused argument
10230 
10231   Options Database Key:
10232 . -dm_plex_monitor_throughput - Activate the monitor
10233 
10234   Level: developer
10235 
10236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10237 @*/
10238 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10239 {
10240   PetscLogHandler default_handler;
10241 
10242   PetscFunctionBegin;
10243   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10244   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10245   if (default_handler) {
10246     PetscLogEvent      event;
10247     PetscEventPerfInfo eventInfo;
10248     PetscReal          cellRate, flopRate;
10249     PetscInt           cStart, cEnd, Nf, N;
10250     const char        *name;
10251 
10252     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10253     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10254     PetscCall(DMGetNumFields(dm, &Nf));
10255     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10256     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10257     N        = (cEnd - cStart) * Nf * eventInfo.count;
10258     flopRate = eventInfo.flops / eventInfo.time;
10259     cellRate = N / eventInfo.time;
10260     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)));
10261   } else {
10262     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off or the default log handler is not running. Reconfigure using --with-log and run with -log_view.");
10263   }
10264   PetscFunctionReturn(PETSC_SUCCESS);
10265 }
10266