xref: /petsc/src/dm/impls/plex/plex.c (revision de8de29fe653896e98bd9cf55f6b93d81eec974b)
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 PetscBool  Plexcite       = PETSC_FALSE;
17 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
18                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
19                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
20                             "journal   = {SIAM Journal on Scientific Computing},\n"
21                             "volume    = {38},\n"
22                             "number    = {5},\n"
23                             "pages     = {S143--S155},\n"
24                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
25                             "doi       = {10.1137/15M1026092},\n"
26                             "year      = {2016},\n"
27                             "petsc_uses={DMPlex},\n}\n";
28 
29 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
30 
31 /*@
32   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
33 
34   Input Parameter:
35 . dm - The `DMPLEX` object
36 
37   Output Parameter:
38 . simplex - Flag checking for a simplex
39 
40   Level: intermediate
41 
42   Note:
43   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
44   If the mesh has no cells, this returns `PETSC_FALSE`.
45 
46 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
47 @*/
48 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
49 {
50   DMPolytopeType ct;
51   PetscInt       cStart, cEnd;
52 
53   PetscFunctionBegin;
54   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
55   if (cEnd <= cStart) {
56     *simplex = PETSC_FALSE;
57     PetscFunctionReturn(PETSC_SUCCESS);
58   }
59   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
60   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
64 /*@
65   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
66 
67   Input Parameters:
68 + dm     - The `DMPLEX` object
69 - height - The cell height in the Plex, 0 is the default
70 
71   Output Parameters:
72 + cStart - The first "normal" cell
73 - cEnd   - The upper bound on "normal"" cells
74 
75   Level: developer
76 
77   Note:
78   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
79 
80 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
81 @*/
82 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
83 {
84   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
85   PetscInt       cS, cE, c;
86 
87   PetscFunctionBegin;
88   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
89   for (c = cS; c < cE; ++c) {
90     DMPolytopeType cct;
91 
92     PetscCall(DMPlexGetCellType(dm, c, &cct));
93     if ((PetscInt)cct < 0) break;
94     switch (cct) {
95     case DM_POLYTOPE_POINT:
96     case DM_POLYTOPE_SEGMENT:
97     case DM_POLYTOPE_TRIANGLE:
98     case DM_POLYTOPE_QUADRILATERAL:
99     case DM_POLYTOPE_TETRAHEDRON:
100     case DM_POLYTOPE_HEXAHEDRON:
101       ct = cct;
102       break;
103     default:
104       break;
105     }
106     if (ct != DM_POLYTOPE_UNKNOWN) break;
107   }
108   if (ct != DM_POLYTOPE_UNKNOWN) {
109     DMLabel ctLabel;
110 
111     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
112     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
113     // Reset label for fast lookup
114     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
115   }
116   if (cStart) *cStart = cS;
117   if (cEnd) *cEnd = cE;
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
122 {
123   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
124   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
125 
126   PetscFunctionBegin;
127   *ft = PETSC_VTK_INVALID;
128   PetscCall(DMGetCoordinateDim(dm, &cdim));
129   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
130   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
131   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
132   if (field >= 0) {
133     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
134     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
135   } else {
136     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
137     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
138   }
139   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
140   if (globalvcdof[0]) {
141     *sStart = vStart;
142     *sEnd   = vEnd;
143     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
144     else *ft = PETSC_VTK_POINT_FIELD;
145   } else if (globalvcdof[1]) {
146     *sStart = cStart;
147     *sEnd   = cEnd;
148     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
149     else *ft = PETSC_VTK_CELL_FIELD;
150   } else {
151     if (field >= 0) {
152       const char *fieldname;
153 
154       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
155       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
156     } else {
157       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
158     }
159   }
160   PetscFunctionReturn(PETSC_SUCCESS);
161 }
162 
163 /*@
164   DMPlexVecView1D - Plot many 1D solutions on the same line graph
165 
166   Collective
167 
168   Input Parameters:
169 + dm     - The `DMPLEX` object
170 . n      - The number of vectors
171 . u      - The array of local vectors
172 - viewer - The `PetscViewer`
173 
174   Level: advanced
175 
176 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
177 @*/
178 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
179 {
180   PetscDS            ds;
181   PetscDraw          draw = NULL;
182   PetscDrawLG        lg;
183   Vec                coordinates;
184   const PetscScalar *coords, **sol;
185   PetscReal         *vals;
186   PetscInt          *Nc;
187   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
188   char             **names;
189 
190   PetscFunctionBegin;
191   PetscCall(DMGetDS(dm, &ds));
192   PetscCall(PetscDSGetNumFields(ds, &Nf));
193   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
194   PetscCall(PetscDSGetComponents(ds, &Nc));
195 
196   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
197   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
198   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
199 
200   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
201   for (i = 0, l = 0; i < n; ++i) {
202     const char *vname;
203 
204     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
205     for (f = 0; f < Nf; ++f) {
206       PetscObject disc;
207       const char *fname;
208       char        tmpname[PETSC_MAX_PATH_LEN];
209 
210       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
211       /* TODO Create names for components */
212       for (c = 0; c < Nc[f]; ++c, ++l) {
213         PetscCall(PetscObjectGetName(disc, &fname));
214         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
215         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
216         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
217         PetscCall(PetscStrallocpy(tmpname, &names[l]));
218       }
219     }
220   }
221   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
222   /* Just add P_1 support for now */
223   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
224   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
225   PetscCall(VecGetArrayRead(coordinates, &coords));
226   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
227   for (v = vStart; v < vEnd; ++v) {
228     PetscScalar *x, *svals;
229 
230     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
231     for (i = 0; i < n; ++i) {
232       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
233       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
234     }
235     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
236   }
237   PetscCall(VecRestoreArrayRead(coordinates, &coords));
238   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
239   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
240   PetscCall(PetscFree3(sol, names, vals));
241 
242   PetscCall(PetscDrawLGDraw(lg));
243   PetscCall(PetscDrawLGDestroy(&lg));
244   PetscFunctionReturn(PETSC_SUCCESS);
245 }
246 
247 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
248 {
249   DM dm;
250 
251   PetscFunctionBegin;
252   PetscCall(VecGetDM(u, &dm));
253   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
258 {
259   DM                 dm;
260   PetscSection       s;
261   PetscDraw          draw, popup;
262   DM                 cdm;
263   PetscSection       coordSection;
264   Vec                coordinates;
265   const PetscScalar *array;
266   PetscReal          lbound[3], ubound[3];
267   PetscReal          vbound[2], time;
268   PetscBool          flg;
269   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
270   const char        *name;
271   char               title[PETSC_MAX_PATH_LEN];
272 
273   PetscFunctionBegin;
274   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
275   PetscCall(VecGetDM(v, &dm));
276   PetscCall(DMGetCoordinateDim(dm, &dim));
277   PetscCall(DMGetLocalSection(dm, &s));
278   PetscCall(PetscSectionGetNumFields(s, &Nf));
279   PetscCall(DMGetCoarsenLevel(dm, &level));
280   PetscCall(DMGetCoordinateDM(dm, &cdm));
281   PetscCall(DMGetLocalSection(cdm, &coordSection));
282   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
283   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
284   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
285 
286   PetscCall(PetscObjectGetName((PetscObject)v, &name));
287   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
288 
289   PetscCall(VecGetLocalSize(coordinates, &N));
290   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
291   PetscCall(PetscDrawClear(draw));
292 
293   /* Could implement something like DMDASelectFields() */
294   for (f = 0; f < Nf; ++f) {
295     DM          fdm = dm;
296     Vec         fv  = v;
297     IS          fis;
298     char        prefix[PETSC_MAX_PATH_LEN];
299     const char *fname;
300 
301     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
302     PetscCall(PetscSectionGetFieldName(s, f, &fname));
303 
304     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
305     else prefix[0] = '\0';
306     if (Nf > 1) {
307       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
308       PetscCall(VecGetSubVector(v, fis, &fv));
309       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
310       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
311     }
312     for (comp = 0; comp < Nc; ++comp, ++w) {
313       PetscInt nmax = 2;
314 
315       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
316       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
317       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
318       PetscCall(PetscDrawSetTitle(draw, title));
319 
320       /* TODO Get max and min only for this component */
321       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
322       if (!flg) {
323         PetscCall(VecMin(fv, NULL, &vbound[0]));
324         PetscCall(VecMax(fv, NULL, &vbound[1]));
325         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
326       }
327 
328       PetscCall(PetscDrawGetPopup(draw, &popup));
329       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
330       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
331       PetscCall(VecGetArrayRead(fv, &array));
332       for (c = cStart; c < cEnd; ++c) {
333         PetscScalar       *coords = NULL, *a = NULL;
334         const PetscScalar *coords_arr;
335         PetscBool          isDG;
336         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
337 
338         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
339         if (a) {
340           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
341           color[1] = color[2] = color[3] = color[0];
342         } else {
343           PetscScalar *vals = NULL;
344           PetscInt     numVals, va;
345 
346           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
347           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);
348           switch (numVals / Nc) {
349           case 3: /* P1 Triangle */
350           case 4: /* P1 Quadrangle */
351             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
352             break;
353           case 6: /* P2 Triangle */
354           case 8: /* P2 Quadrangle */
355             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
356             break;
357           default:
358             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
359           }
360           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
361         }
362         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
363         switch (numCoords) {
364         case 6:
365         case 12: /* Localized triangle */
366           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]));
367           break;
368         case 8:
369         case 16: /* Localized quadrilateral */
370           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]));
371           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]));
372           break;
373         default:
374           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
375         }
376         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
377       }
378       PetscCall(VecRestoreArrayRead(fv, &array));
379       PetscCall(PetscDrawFlush(draw));
380       PetscCall(PetscDrawPause(draw));
381       PetscCall(PetscDrawSave(draw));
382     }
383     if (Nf > 1) {
384       PetscCall(VecRestoreSubVector(v, fis, &fv));
385       PetscCall(ISDestroy(&fis));
386       PetscCall(DMDestroy(&fdm));
387     }
388   }
389   PetscFunctionReturn(PETSC_SUCCESS);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
393 {
394   DM        dm;
395   PetscDraw draw;
396   PetscInt  dim;
397   PetscBool isnull;
398 
399   PetscFunctionBegin;
400   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
401   PetscCall(PetscDrawIsNull(draw, &isnull));
402   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
403 
404   PetscCall(VecGetDM(v, &dm));
405   PetscCall(DMGetCoordinateDim(dm, &dim));
406   switch (dim) {
407   case 1:
408     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
409     break;
410   case 2:
411     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
412     break;
413   default:
414     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
415   }
416   PetscFunctionReturn(PETSC_SUCCESS);
417 }
418 
419 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
420 {
421   DM                      dm;
422   Vec                     locv;
423   const char             *name;
424   PetscSection            section;
425   PetscInt                pStart, pEnd;
426   PetscInt                numFields;
427   PetscViewerVTKFieldType ft;
428 
429   PetscFunctionBegin;
430   PetscCall(VecGetDM(v, &dm));
431   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
432   PetscCall(PetscObjectGetName((PetscObject)v, &name));
433   PetscCall(PetscObjectSetName((PetscObject)locv, name));
434   PetscCall(VecCopy(v, locv));
435   PetscCall(DMGetLocalSection(dm, &section));
436   PetscCall(PetscSectionGetNumFields(section, &numFields));
437   if (!numFields) {
438     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
439     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
440   } else {
441     PetscInt f;
442 
443     for (f = 0; f < numFields; f++) {
444       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
445       if (ft == PETSC_VTK_INVALID) continue;
446       PetscCall(PetscObjectReference((PetscObject)locv));
447       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
448     }
449     PetscCall(VecDestroy(&locv));
450   }
451   PetscFunctionReturn(PETSC_SUCCESS);
452 }
453 
454 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
455 {
456   DM        dm;
457   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
458 
459   PetscFunctionBegin;
460   PetscCall(VecGetDM(v, &dm));
461   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
462   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
463   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
464   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
465   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
466   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
467   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
468     PetscInt    i, numFields;
469     PetscObject fe;
470     PetscBool   fem  = PETSC_FALSE;
471     Vec         locv = v;
472     const char *name;
473     PetscInt    step;
474     PetscReal   time;
475 
476     PetscCall(DMGetNumFields(dm, &numFields));
477     for (i = 0; i < numFields; i++) {
478       PetscCall(DMGetField(dm, i, NULL, &fe));
479       if (fe->classid == PETSCFE_CLASSID) {
480         fem = PETSC_TRUE;
481         break;
482       }
483     }
484     if (fem) {
485       PetscObject isZero;
486 
487       PetscCall(DMGetLocalVector(dm, &locv));
488       PetscCall(PetscObjectGetName((PetscObject)v, &name));
489       PetscCall(PetscObjectSetName((PetscObject)locv, name));
490       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
491       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
492       PetscCall(VecCopy(v, locv));
493       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
494       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
495     }
496     if (isvtk) {
497       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
498     } else if (ishdf5) {
499 #if defined(PETSC_HAVE_HDF5)
500       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
501 #else
502       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
503 #endif
504     } else if (isdraw) {
505       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
506     } else if (isglvis) {
507       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
508       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
509       PetscCall(VecView_GLVis(locv, viewer));
510     } else if (iscgns) {
511 #if defined(PETSC_HAVE_CGNS)
512       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
513 #else
514       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
515 #endif
516     }
517     if (fem) {
518       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
519       PetscCall(DMRestoreLocalVector(dm, &locv));
520     }
521   } else {
522     PetscBool isseq;
523 
524     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
525     if (isseq) PetscCall(VecView_Seq(v, viewer));
526     else PetscCall(VecView_MPI(v, viewer));
527   }
528   PetscFunctionReturn(PETSC_SUCCESS);
529 }
530 
531 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
532 {
533   DM        dm;
534   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
535 
536   PetscFunctionBegin;
537   PetscCall(VecGetDM(v, &dm));
538   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
539   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
540   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
541   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
542   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
543   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
544   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
545   if (isvtk || isdraw || isglvis || iscgns) {
546     Vec         locv;
547     PetscObject isZero;
548     const char *name;
549 
550     PetscCall(DMGetLocalVector(dm, &locv));
551     PetscCall(PetscObjectGetName((PetscObject)v, &name));
552     PetscCall(PetscObjectSetName((PetscObject)locv, name));
553     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
554     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
555     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
556     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
557     PetscCall(VecView_Plex_Local(locv, viewer));
558     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
559     PetscCall(DMRestoreLocalVector(dm, &locv));
560   } else if (ishdf5) {
561 #if defined(PETSC_HAVE_HDF5)
562     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
563 #else
564     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
565 #endif
566   } else if (isexodusii) {
567 #if defined(PETSC_HAVE_EXODUSII)
568     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
569 #else
570     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
571 #endif
572   } else {
573     PetscBool isseq;
574 
575     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
576     if (isseq) PetscCall(VecView_Seq(v, viewer));
577     else PetscCall(VecView_MPI(v, viewer));
578   }
579   PetscFunctionReturn(PETSC_SUCCESS);
580 }
581 
582 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
583 {
584   DM                dm;
585   MPI_Comm          comm;
586   PetscViewerFormat format;
587   Vec               v;
588   PetscBool         isvtk, ishdf5;
589 
590   PetscFunctionBegin;
591   PetscCall(VecGetDM(originalv, &dm));
592   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
593   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
594   PetscCall(PetscViewerGetFormat(viewer, &format));
595   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
596   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
597   if (format == PETSC_VIEWER_NATIVE) {
598     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
599     /* this need a better fix */
600     if (dm->useNatural) {
601       if (dm->sfNatural) {
602         const char *vecname;
603         PetscInt    n, nroots;
604 
605         PetscCall(VecGetLocalSize(originalv, &n));
606         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
607         if (n == nroots) {
608           PetscCall(DMPlexCreateNaturalVector(dm, &v));
609           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
610           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
611           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
612           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
613         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
614       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
615     } else v = originalv;
616   } else v = originalv;
617 
618   if (ishdf5) {
619 #if defined(PETSC_HAVE_HDF5)
620     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
621 #else
622     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
623 #endif
624   } else if (isvtk) {
625     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
626   } else {
627     PetscBool isseq;
628 
629     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
630     if (isseq) PetscCall(VecView_Seq(v, viewer));
631     else PetscCall(VecView_MPI(v, viewer));
632   }
633   if (v != originalv) PetscCall(VecDestroy(&v));
634   PetscFunctionReturn(PETSC_SUCCESS);
635 }
636 
637 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
638 {
639   DM        dm;
640   PetscBool ishdf5;
641 
642   PetscFunctionBegin;
643   PetscCall(VecGetDM(v, &dm));
644   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
645   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
646   if (ishdf5) {
647     DM          dmBC;
648     Vec         gv;
649     const char *name;
650 
651     PetscCall(DMGetOutputDM(dm, &dmBC));
652     PetscCall(DMGetGlobalVector(dmBC, &gv));
653     PetscCall(PetscObjectGetName((PetscObject)v, &name));
654     PetscCall(PetscObjectSetName((PetscObject)gv, name));
655     PetscCall(VecLoad_Default(gv, viewer));
656     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
657     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
658     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
659   } else PetscCall(VecLoad_Default(v, viewer));
660   PetscFunctionReturn(PETSC_SUCCESS);
661 }
662 
663 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
664 {
665   DM        dm;
666   PetscBool ishdf5, isexodusii;
667 
668   PetscFunctionBegin;
669   PetscCall(VecGetDM(v, &dm));
670   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
671   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
672   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
673   if (ishdf5) {
674 #if defined(PETSC_HAVE_HDF5)
675     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
676 #else
677     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
678 #endif
679   } else if (isexodusii) {
680 #if defined(PETSC_HAVE_EXODUSII)
681     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
682 #else
683     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
684 #endif
685   } else PetscCall(VecLoad_Default(v, viewer));
686   PetscFunctionReturn(PETSC_SUCCESS);
687 }
688 
689 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
690 {
691   DM                dm;
692   PetscViewerFormat format;
693   PetscBool         ishdf5;
694 
695   PetscFunctionBegin;
696   PetscCall(VecGetDM(originalv, &dm));
697   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
698   PetscCall(PetscViewerGetFormat(viewer, &format));
699   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
700   if (format == PETSC_VIEWER_NATIVE) {
701     if (dm->useNatural) {
702       if (dm->sfNatural) {
703         if (ishdf5) {
704 #if defined(PETSC_HAVE_HDF5)
705           Vec         v;
706           const char *vecname;
707 
708           PetscCall(DMPlexCreateNaturalVector(dm, &v));
709           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
710           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
711           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
712           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
713           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
714           PetscCall(VecDestroy(&v));
715 #else
716           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
717 #endif
718         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
719       }
720     } else PetscCall(VecLoad_Default(originalv, viewer));
721   }
722   PetscFunctionReturn(PETSC_SUCCESS);
723 }
724 
725 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
726 {
727   PetscSection       coordSection;
728   Vec                coordinates;
729   DMLabel            depthLabel, celltypeLabel;
730   const char        *name[4];
731   const PetscScalar *a;
732   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
733 
734   PetscFunctionBegin;
735   PetscCall(DMGetDimension(dm, &dim));
736   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
737   PetscCall(DMGetCoordinateSection(dm, &coordSection));
738   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
739   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
740   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
741   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
742   PetscCall(VecGetArrayRead(coordinates, &a));
743   name[0]       = "vertex";
744   name[1]       = "edge";
745   name[dim - 1] = "face";
746   name[dim]     = "cell";
747   for (c = cStart; c < cEnd; ++c) {
748     PetscInt *closure = NULL;
749     PetscInt  closureSize, cl, ct;
750 
751     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
752     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
753     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
754     PetscCall(PetscViewerASCIIPushTab(viewer));
755     for (cl = 0; cl < closureSize * 2; cl += 2) {
756       PetscInt point = closure[cl], depth, dof, off, d, p;
757 
758       if ((point < pStart) || (point >= pEnd)) continue;
759       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
760       if (!dof) continue;
761       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
762       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
763       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
764       for (p = 0; p < dof / dim; ++p) {
765         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
766         for (d = 0; d < dim; ++d) {
767           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
768           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
769         }
770         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
771       }
772       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
773     }
774     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
775     PetscCall(PetscViewerASCIIPopTab(viewer));
776   }
777   PetscCall(VecRestoreArrayRead(coordinates, &a));
778   PetscFunctionReturn(PETSC_SUCCESS);
779 }
780 
781 typedef enum {
782   CS_CARTESIAN,
783   CS_POLAR,
784   CS_CYLINDRICAL,
785   CS_SPHERICAL
786 } CoordSystem;
787 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
788 
789 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
790 {
791   PetscInt i;
792 
793   PetscFunctionBegin;
794   if (dim > 3) {
795     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
796   } else {
797     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
798 
799     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
800     switch (cs) {
801     case CS_CARTESIAN:
802       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
803       break;
804     case CS_POLAR:
805       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
806       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
807       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
808       break;
809     case CS_CYLINDRICAL:
810       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
811       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
812       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
813       trcoords[2] = coords[2];
814       break;
815     case CS_SPHERICAL:
816       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
817       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
818       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
819       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
820       break;
821     }
822     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
823   }
824   PetscFunctionReturn(PETSC_SUCCESS);
825 }
826 
827 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
828 {
829   DM_Plex          *mesh = (DM_Plex *)dm->data;
830   DM                cdm, cdmCell;
831   PetscSection      coordSection, coordSectionCell;
832   Vec               coordinates, coordinatesCell;
833   PetscViewerFormat format;
834 
835   PetscFunctionBegin;
836   PetscCall(PetscViewerGetFormat(viewer, &format));
837   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
838     const char *name;
839     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
840     PetscInt    pStart, pEnd, p, numLabels, l;
841     PetscMPIInt rank, size;
842 
843     PetscCall(DMGetCoordinateDM(dm, &cdm));
844     PetscCall(DMGetCoordinateSection(dm, &coordSection));
845     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
846     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
847     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
848     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
849     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
850     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
851     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
852     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
853     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
854     PetscCall(DMGetDimension(dm, &dim));
855     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
856     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
857     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
858     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
859     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
860     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
861     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
862     for (p = pStart; p < pEnd; ++p) {
863       PetscInt dof, off, s;
864 
865       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
866       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
867       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
868     }
869     PetscCall(PetscViewerFlush(viewer));
870     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
871     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
872     for (p = pStart; p < pEnd; ++p) {
873       PetscInt dof, off, c;
874 
875       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
876       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
877       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]));
878     }
879     PetscCall(PetscViewerFlush(viewer));
880     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
881     if (coordSection && coordinates) {
882       CoordSystem        cs = CS_CARTESIAN;
883       const PetscScalar *array, *arrayCell = NULL;
884       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
885       PetscMPIInt        rank;
886       const char        *name;
887 
888       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
889       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
890       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
891       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
892       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
893       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
894       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
895       pStart = PetscMin(pvStart, pcStart);
896       pEnd   = PetscMax(pvEnd, pcEnd);
897       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
898       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
899       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
900       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
901 
902       PetscCall(VecGetArrayRead(coordinates, &array));
903       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
904       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
905       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
906       for (p = pStart; p < pEnd; ++p) {
907         PetscInt dof, off;
908 
909         if (p >= pvStart && p < pvEnd) {
910           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
911           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
912           if (dof) {
913             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
914             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
915             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
916           }
917         }
918         if (cdmCell && p >= pcStart && p < pcEnd) {
919           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
920           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
921           if (dof) {
922             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
923             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
924             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
925           }
926         }
927       }
928       PetscCall(PetscViewerFlush(viewer));
929       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
930       PetscCall(VecRestoreArrayRead(coordinates, &array));
931       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
932     }
933     PetscCall(DMGetNumLabels(dm, &numLabels));
934     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
935     for (l = 0; l < numLabels; ++l) {
936       DMLabel     label;
937       PetscBool   isdepth;
938       const char *name;
939 
940       PetscCall(DMGetLabelName(dm, l, &name));
941       PetscCall(PetscStrcmp(name, "depth", &isdepth));
942       if (isdepth) continue;
943       PetscCall(DMGetLabel(dm, name, &label));
944       PetscCall(DMLabelView(label, viewer));
945     }
946     if (size > 1) {
947       PetscSF sf;
948 
949       PetscCall(DMGetPointSF(dm, &sf));
950       PetscCall(PetscSFView(sf, viewer));
951     }
952     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
953     PetscCall(PetscViewerFlush(viewer));
954   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
955     const char  *name, *color;
956     const char  *defcolors[3]  = {"gray", "orange", "green"};
957     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
958     char         lname[PETSC_MAX_PATH_LEN];
959     PetscReal    scale      = 2.0;
960     PetscReal    tikzscale  = 1.0;
961     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
962     double       tcoords[3];
963     PetscScalar *coords;
964     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
965     PetscMPIInt  rank, size;
966     char       **names, **colors, **lcolors;
967     PetscBool    flg, lflg;
968     PetscBT      wp = NULL;
969     PetscInt     pEnd, pStart;
970 
971     PetscCall(DMGetCoordinateDM(dm, &cdm));
972     PetscCall(DMGetCoordinateSection(dm, &coordSection));
973     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
974     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
975     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
976     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
977     PetscCall(DMGetDimension(dm, &dim));
978     PetscCall(DMPlexGetDepth(dm, &depth));
979     PetscCall(DMGetNumLabels(dm, &numLabels));
980     numLabels  = PetscMax(numLabels, 10);
981     numColors  = 10;
982     numLColors = 10;
983     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
984     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
985     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
986     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
987     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
988     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
989     n = 4;
990     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
991     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
992     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
993     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
994     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
995     if (!useLabels) numLabels = 0;
996     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
997     if (!useColors) {
998       numColors = 3;
999       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1000     }
1001     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1002     if (!useColors) {
1003       numLColors = 4;
1004       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1005     }
1006     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1007     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1008     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1009     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1010     if (depth < dim) plotEdges = PETSC_FALSE;
1011     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1012 
1013     /* filter points with labelvalue != labeldefaultvalue */
1014     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1015     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1016     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1017     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1018     if (lflg) {
1019       DMLabel lbl;
1020 
1021       PetscCall(DMGetLabel(dm, lname, &lbl));
1022       if (lbl) {
1023         PetscInt val, defval;
1024 
1025         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1026         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1027         for (c = pStart; c < pEnd; c++) {
1028           PetscInt *closure = NULL;
1029           PetscInt  closureSize;
1030 
1031           PetscCall(DMLabelGetValue(lbl, c, &val));
1032           if (val == defval) continue;
1033 
1034           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1035           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1036           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1037         }
1038       }
1039     }
1040 
1041     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1042     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1043     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1044     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1045 \\documentclass[tikz]{standalone}\n\n\
1046 \\usepackage{pgflibraryshapes}\n\
1047 \\usetikzlibrary{backgrounds}\n\
1048 \\usetikzlibrary{arrows}\n\
1049 \\begin{document}\n"));
1050     if (size > 1) {
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1052       for (p = 0; p < size; ++p) {
1053         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1054         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1055       }
1056       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1057     }
1058     if (drawHasse) {
1059       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1060 
1061       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1062       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1063       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1064       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1065       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1066       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1067       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1068       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1069       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1070       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1071       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1072       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1073     }
1074     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1075 
1076     /* Plot vertices */
1077     PetscCall(VecGetArray(coordinates, &coords));
1078     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1079     for (v = vStart; v < vEnd; ++v) {
1080       PetscInt  off, dof, d;
1081       PetscBool isLabeled = PETSC_FALSE;
1082 
1083       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1084       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1085       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1086       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1087       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1088       for (d = 0; d < dof; ++d) {
1089         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1090         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1091       }
1092       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1093       if (dim == 3) {
1094         PetscReal tmp = tcoords[1];
1095         tcoords[1]    = tcoords[2];
1096         tcoords[2]    = -tmp;
1097       }
1098       for (d = 0; d < dof; ++d) {
1099         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1100         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1101       }
1102       if (drawHasse) color = colors[0 % numColors];
1103       else color = colors[rank % numColors];
1104       for (l = 0; l < numLabels; ++l) {
1105         PetscInt val;
1106         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1107         if (val >= 0) {
1108           color     = lcolors[l % numLColors];
1109           isLabeled = PETSC_TRUE;
1110           break;
1111         }
1112       }
1113       if (drawNumbers[0]) {
1114         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1115       } else if (drawColors[0]) {
1116         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1117       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1118     }
1119     PetscCall(VecRestoreArray(coordinates, &coords));
1120     PetscCall(PetscViewerFlush(viewer));
1121     /* Plot edges */
1122     if (plotEdges) {
1123       PetscCall(VecGetArray(coordinates, &coords));
1124       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1125       for (e = eStart; e < eEnd; ++e) {
1126         const PetscInt *cone;
1127         PetscInt        coneSize, offA, offB, dof, d;
1128 
1129         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1130         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1131         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1132         PetscCall(DMPlexGetCone(dm, e, &cone));
1133         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1134         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1135         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1136         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1137         for (d = 0; d < dof; ++d) {
1138           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1139           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1140         }
1141         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1142         if (dim == 3) {
1143           PetscReal tmp = tcoords[1];
1144           tcoords[1]    = tcoords[2];
1145           tcoords[2]    = -tmp;
1146         }
1147         for (d = 0; d < dof; ++d) {
1148           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1149           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1150         }
1151         if (drawHasse) color = colors[1 % numColors];
1152         else color = colors[rank % numColors];
1153         for (l = 0; l < numLabels; ++l) {
1154           PetscInt val;
1155           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1156           if (val >= 0) {
1157             color = lcolors[l % numLColors];
1158             break;
1159           }
1160         }
1161         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1162       }
1163       PetscCall(VecRestoreArray(coordinates, &coords));
1164       PetscCall(PetscViewerFlush(viewer));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1166     }
1167     /* Plot cells */
1168     if (dim == 3 || !drawNumbers[1]) {
1169       for (e = eStart; e < eEnd; ++e) {
1170         const PetscInt *cone;
1171 
1172         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1173         color = colors[rank % numColors];
1174         for (l = 0; l < numLabels; ++l) {
1175           PetscInt val;
1176           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1177           if (val >= 0) {
1178             color = lcolors[l % numLColors];
1179             break;
1180           }
1181         }
1182         PetscCall(DMPlexGetCone(dm, e, &cone));
1183         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1184       }
1185     } else {
1186       DMPolytopeType ct;
1187 
1188       /* Drawing a 2D polygon */
1189       for (c = cStart; c < cEnd; ++c) {
1190         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1191         PetscCall(DMPlexGetCellType(dm, c, &ct));
1192         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1193           const PetscInt *cone;
1194           PetscInt        coneSize, e;
1195 
1196           PetscCall(DMPlexGetCone(dm, c, &cone));
1197           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1198           for (e = 0; e < coneSize; ++e) {
1199             const PetscInt *econe;
1200 
1201             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1202             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));
1203           }
1204         } else {
1205           PetscInt *closure = NULL;
1206           PetscInt  closureSize, Nv = 0, v;
1207 
1208           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1209           for (p = 0; p < closureSize * 2; p += 2) {
1210             const PetscInt point = closure[p];
1211 
1212             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1213           }
1214           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1215           for (v = 0; v <= Nv; ++v) {
1216             const PetscInt vertex = closure[v % Nv];
1217 
1218             if (v > 0) {
1219               if (plotEdges) {
1220                 const PetscInt *edge;
1221                 PetscInt        endpoints[2], ne;
1222 
1223                 endpoints[0] = closure[v - 1];
1224                 endpoints[1] = vertex;
1225                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1226                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1227                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1228                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1229               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1230             }
1231             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1232           }
1233           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1234           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1235         }
1236       }
1237     }
1238     for (c = cStart; c < cEnd; ++c) {
1239       double             ccoords[3] = {0.0, 0.0, 0.0};
1240       PetscBool          isLabeled  = PETSC_FALSE;
1241       PetscScalar       *cellCoords = NULL;
1242       const PetscScalar *array;
1243       PetscInt           numCoords, cdim, d;
1244       PetscBool          isDG;
1245 
1246       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1247       PetscCall(DMGetCoordinateDim(dm, &cdim));
1248       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1249       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1250       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1251       for (p = 0; p < numCoords / cdim; ++p) {
1252         for (d = 0; d < cdim; ++d) {
1253           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1254           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1255         }
1256         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1257         if (cdim == 3) {
1258           PetscReal tmp = tcoords[1];
1259           tcoords[1]    = tcoords[2];
1260           tcoords[2]    = -tmp;
1261         }
1262         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1263       }
1264       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1265       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1266       for (d = 0; d < cdim; ++d) {
1267         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1268         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1269       }
1270       if (drawHasse) color = colors[depth % numColors];
1271       else color = colors[rank % numColors];
1272       for (l = 0; l < numLabels; ++l) {
1273         PetscInt val;
1274         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1275         if (val >= 0) {
1276           color     = lcolors[l % numLColors];
1277           isLabeled = PETSC_TRUE;
1278           break;
1279         }
1280       }
1281       if (drawNumbers[dim]) {
1282         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1283       } else if (drawColors[dim]) {
1284         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1285       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1286     }
1287     if (drawHasse) {
1288       color = colors[depth % numColors];
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1294 
1295       color = colors[1 % numColors];
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1297       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1298       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1299       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1300       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1301 
1302       color = colors[0 % numColors];
1303       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1304       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1305       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1306       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1307       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1308 
1309       for (p = pStart; p < pEnd; ++p) {
1310         const PetscInt *cone;
1311         PetscInt        coneSize, cp;
1312 
1313         PetscCall(DMPlexGetCone(dm, p, &cone));
1314         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1315         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1316       }
1317     }
1318     PetscCall(PetscViewerFlush(viewer));
1319     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1320     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1321     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1322     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1323     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1324     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1325     PetscCall(PetscFree3(names, colors, lcolors));
1326     PetscCall(PetscBTDestroy(&wp));
1327   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1328     Vec                    cown, acown;
1329     VecScatter             sct;
1330     ISLocalToGlobalMapping g2l;
1331     IS                     gid, acis;
1332     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1333     MPI_Group              ggroup, ngroup;
1334     PetscScalar           *array, nid;
1335     const PetscInt        *idxs;
1336     PetscInt              *idxs2, *start, *adjacency, *work;
1337     PetscInt64             lm[3], gm[3];
1338     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1339     PetscMPIInt            d1, d2, rank;
1340 
1341     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1342     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1343 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1344     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1345 #endif
1346     if (ncomm != MPI_COMM_NULL) {
1347       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1348       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1349       d1 = 0;
1350       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1351       nid = d2;
1352       PetscCallMPI(MPI_Group_free(&ggroup));
1353       PetscCallMPI(MPI_Group_free(&ngroup));
1354       PetscCallMPI(MPI_Comm_free(&ncomm));
1355     } else nid = 0.0;
1356 
1357     /* Get connectivity */
1358     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1359     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1360 
1361     /* filter overlapped local cells */
1362     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1363     PetscCall(ISGetIndices(gid, &idxs));
1364     PetscCall(ISGetLocalSize(gid, &cum));
1365     PetscCall(PetscMalloc1(cum, &idxs2));
1366     for (c = cStart, cum = 0; c < cEnd; c++) {
1367       if (idxs[c - cStart] < 0) continue;
1368       idxs2[cum++] = idxs[c - cStart];
1369     }
1370     PetscCall(ISRestoreIndices(gid, &idxs));
1371     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1372     PetscCall(ISDestroy(&gid));
1373     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1374 
1375     /* support for node-aware cell locality */
1376     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1377     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1378     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1379     PetscCall(VecGetArray(cown, &array));
1380     for (c = 0; c < numVertices; c++) array[c] = nid;
1381     PetscCall(VecRestoreArray(cown, &array));
1382     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1383     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1384     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1385     PetscCall(ISDestroy(&acis));
1386     PetscCall(VecScatterDestroy(&sct));
1387     PetscCall(VecDestroy(&cown));
1388 
1389     /* compute edgeCut */
1390     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1391     PetscCall(PetscMalloc1(cum, &work));
1392     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1393     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1394     PetscCall(ISDestroy(&gid));
1395     PetscCall(VecGetArray(acown, &array));
1396     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1397       PetscInt totl;
1398 
1399       totl = start[c + 1] - start[c];
1400       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1401       for (i = 0; i < totl; i++) {
1402         if (work[i] < 0) {
1403           ect += 1;
1404           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1405         }
1406       }
1407     }
1408     PetscCall(PetscFree(work));
1409     PetscCall(VecRestoreArray(acown, &array));
1410     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1411     lm[1] = -numVertices;
1412     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1413     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1414     lm[0] = ect;                     /* edgeCut */
1415     lm[1] = ectn;                    /* node-aware edgeCut */
1416     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1417     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1418     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1419 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1420     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1421 #else
1422     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1423 #endif
1424     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1425     PetscCall(PetscFree(start));
1426     PetscCall(PetscFree(adjacency));
1427     PetscCall(VecDestroy(&acown));
1428   } else {
1429     const char    *name;
1430     PetscInt      *sizes, *hybsizes, *ghostsizes;
1431     PetscInt       locDepth, depth, cellHeight, dim, d;
1432     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1433     PetscInt       numLabels, l, maxSize = 17;
1434     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1435     MPI_Comm       comm;
1436     PetscMPIInt    size, rank;
1437 
1438     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1439     PetscCallMPI(MPI_Comm_size(comm, &size));
1440     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1441     PetscCall(DMGetDimension(dm, &dim));
1442     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1443     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1444     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1445     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1446     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1447     PetscCall(DMPlexGetDepth(dm, &locDepth));
1448     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1449     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1450     gcNum = gcEnd - gcStart;
1451     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1452     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1453     for (d = 0; d <= depth; d++) {
1454       PetscInt Nc[2] = {0, 0}, ict;
1455 
1456       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1457       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1458       ict = ct0;
1459       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1460       ct0 = (DMPolytopeType)ict;
1461       for (p = pStart; p < pEnd; ++p) {
1462         DMPolytopeType ct;
1463 
1464         PetscCall(DMPlexGetCellType(dm, p, &ct));
1465         if (ct == ct0) ++Nc[0];
1466         else ++Nc[1];
1467       }
1468       if (size < maxSize) {
1469         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1470         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1471         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1472         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1473         for (p = 0; p < size; ++p) {
1474           if (rank == 0) {
1475             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1476             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1477             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1478           }
1479         }
1480       } else {
1481         PetscInt locMinMax[2];
1482 
1483         locMinMax[0] = Nc[0] + Nc[1];
1484         locMinMax[1] = Nc[0] + Nc[1];
1485         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1486         locMinMax[0] = Nc[1];
1487         locMinMax[1] = Nc[1];
1488         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1489         if (d == depth) {
1490           locMinMax[0] = gcNum;
1491           locMinMax[1] = gcNum;
1492           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1493         }
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1495         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1496         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1497         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1498       }
1499       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1500     }
1501     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1502     {
1503       const PetscReal *maxCell;
1504       const PetscReal *L;
1505       PetscBool        localized;
1506 
1507       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1508       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1509       if (L || localized) {
1510         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1511         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1512         if (L) {
1513           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1514           for (d = 0; d < dim; ++d) {
1515             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1516             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1517           }
1518           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1519         }
1520         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1521         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1522       }
1523     }
1524     PetscCall(DMGetNumLabels(dm, &numLabels));
1525     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1526     for (l = 0; l < numLabels; ++l) {
1527       DMLabel         label;
1528       const char     *name;
1529       IS              valueIS;
1530       const PetscInt *values;
1531       PetscInt        numValues, v;
1532 
1533       PetscCall(DMGetLabelName(dm, l, &name));
1534       PetscCall(DMGetLabel(dm, name, &label));
1535       PetscCall(DMLabelGetNumValues(label, &numValues));
1536       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1537       PetscCall(DMLabelGetValueIS(label, &valueIS));
1538       PetscCall(ISGetIndices(valueIS, &values));
1539       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1540       for (v = 0; v < numValues; ++v) {
1541         PetscInt size;
1542 
1543         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1544         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1545         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1546       }
1547       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1548       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1549       PetscCall(ISRestoreIndices(valueIS, &values));
1550       PetscCall(ISDestroy(&valueIS));
1551     }
1552     {
1553       char    **labelNames;
1554       PetscInt  Nl = numLabels;
1555       PetscBool flg;
1556 
1557       PetscCall(PetscMalloc1(Nl, &labelNames));
1558       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1559       for (l = 0; l < Nl; ++l) {
1560         DMLabel label;
1561 
1562         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1563         if (flg) {
1564           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1565           PetscCall(DMLabelView(label, viewer));
1566         }
1567         PetscCall(PetscFree(labelNames[l]));
1568       }
1569       PetscCall(PetscFree(labelNames));
1570     }
1571     /* If no fields are specified, people do not want to see adjacency */
1572     if (dm->Nf) {
1573       PetscInt f;
1574 
1575       for (f = 0; f < dm->Nf; ++f) {
1576         const char *name;
1577 
1578         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1579         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1580         PetscCall(PetscViewerASCIIPushTab(viewer));
1581         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1582         if (dm->fields[f].adjacency[0]) {
1583           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1584           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1585         } else {
1586           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1587           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1588         }
1589         PetscCall(PetscViewerASCIIPopTab(viewer));
1590       }
1591     }
1592     PetscCall(DMGetCoarseDM(dm, &cdm));
1593     if (cdm) {
1594       PetscCall(PetscViewerASCIIPushTab(viewer));
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1596       PetscCall(DMPlexView_Ascii(cdm, viewer));
1597       PetscCall(PetscViewerASCIIPopTab(viewer));
1598     }
1599   }
1600   PetscFunctionReturn(PETSC_SUCCESS);
1601 }
1602 
1603 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1604 {
1605   DMPolytopeType ct;
1606   PetscMPIInt    rank;
1607   PetscInt       cdim;
1608 
1609   PetscFunctionBegin;
1610   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1611   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1612   PetscCall(DMGetCoordinateDim(dm, &cdim));
1613   switch (ct) {
1614   case DM_POLYTOPE_SEGMENT:
1615   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1616     switch (cdim) {
1617     case 1: {
1618       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1619       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1620 
1621       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1622       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1623       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1624     } break;
1625     case 2: {
1626       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1627       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1628       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1629 
1630       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1631       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));
1632       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));
1633     } break;
1634     default:
1635       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1636     }
1637     break;
1638   case DM_POLYTOPE_TRIANGLE:
1639     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));
1640     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1641     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1643     break;
1644   case DM_POLYTOPE_QUADRILATERAL:
1645     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));
1646     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));
1647     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1648     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1649     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1650     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1651     break;
1652   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1653     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));
1654     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));
1655     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1656     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1657     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1658     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1659     break;
1660   case DM_POLYTOPE_FV_GHOST:
1661     break;
1662   default:
1663     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1664   }
1665   PetscFunctionReturn(PETSC_SUCCESS);
1666 }
1667 
1668 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1669 {
1670   DMPolytopeType ct;
1671   PetscReal      centroid[2] = {0., 0.};
1672   PetscMPIInt    rank;
1673   PetscInt       fillColor, v, e, d;
1674 
1675   PetscFunctionBegin;
1676   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1677   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1678   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1679   switch (ct) {
1680   case DM_POLYTOPE_TRIANGLE: {
1681     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1682 
1683     for (v = 0; v < 3; ++v) {
1684       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1685       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1686     }
1687     for (e = 0; e < 3; ++e) {
1688       refCoords[0] = refVertices[e * 2 + 0];
1689       refCoords[1] = refVertices[e * 2 + 1];
1690       for (d = 1; d <= edgeDiv; ++d) {
1691         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1692         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1693       }
1694       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1695       for (d = 0; d < edgeDiv; ++d) {
1696         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));
1697         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1698       }
1699     }
1700   } break;
1701   default:
1702     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1703   }
1704   PetscFunctionReturn(PETSC_SUCCESS);
1705 }
1706 
1707 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1708 {
1709   PetscDraw    draw;
1710   DM           cdm;
1711   PetscSection coordSection;
1712   Vec          coordinates;
1713   PetscReal    xyl[3], xyr[3];
1714   PetscReal   *refCoords, *edgeCoords;
1715   PetscBool    isnull, drawAffine = PETSC_TRUE;
1716   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1717 
1718   PetscFunctionBegin;
1719   PetscCall(DMGetCoordinateDim(dm, &dim));
1720   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1721   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1722   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1723   PetscCall(DMGetCoordinateDM(dm, &cdm));
1724   PetscCall(DMGetLocalSection(cdm, &coordSection));
1725   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1726   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1728 
1729   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1730   PetscCall(PetscDrawIsNull(draw, &isnull));
1731   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1732   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1733 
1734   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1735   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1736   PetscCall(PetscDrawClear(draw));
1737 
1738   for (c = cStart; c < cEnd; ++c) {
1739     PetscScalar       *coords = NULL;
1740     const PetscScalar *coords_arr;
1741     PetscInt           numCoords;
1742     PetscBool          isDG;
1743 
1744     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1745     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1746     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1747     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1748   }
1749   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1750   PetscCall(PetscDrawFlush(draw));
1751   PetscCall(PetscDrawPause(draw));
1752   PetscCall(PetscDrawSave(draw));
1753   PetscFunctionReturn(PETSC_SUCCESS);
1754 }
1755 
1756 #if defined(PETSC_HAVE_EXODUSII)
1757   #include <exodusII.h>
1758   #include <petscviewerexodusii.h>
1759 #endif
1760 
1761 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1762 {
1763   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1764   char      name[PETSC_MAX_PATH_LEN];
1765 
1766   PetscFunctionBegin;
1767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1768   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1769   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1771   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1776   if (iascii) {
1777     PetscViewerFormat format;
1778     PetscCall(PetscViewerGetFormat(viewer, &format));
1779     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1780     else PetscCall(DMPlexView_Ascii(dm, viewer));
1781   } else if (ishdf5) {
1782 #if defined(PETSC_HAVE_HDF5)
1783     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1784 #else
1785     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1786 #endif
1787   } else if (isvtk) {
1788     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1789   } else if (isdraw) {
1790     PetscCall(DMPlexView_Draw(dm, viewer));
1791   } else if (isglvis) {
1792     PetscCall(DMPlexView_GLVis(dm, viewer));
1793 #if defined(PETSC_HAVE_EXODUSII)
1794   } else if (isexodus) {
1795     /*
1796       exodusII requires that all sets be part of exactly one cell set.
1797       If the dm does not have a "Cell Sets" label defined, we create one
1798       with ID 1, containing all cells.
1799       Note that if the Cell Sets label is defined but does not cover all cells,
1800       we may still have a problem. This should probably be checked here or in the viewer;
1801     */
1802     PetscInt numCS;
1803     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1804     if (!numCS) {
1805       PetscInt cStart, cEnd, c;
1806       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1807       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1808       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1809     }
1810     PetscCall(DMView_PlexExodusII(dm, viewer));
1811 #endif
1812 #if defined(PETSC_HAVE_CGNS)
1813   } else if (iscgns) {
1814     PetscCall(DMView_PlexCGNS(dm, viewer));
1815 #endif
1816   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1817   /* Optionally view the partition */
1818   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1819   if (flg) {
1820     Vec ranks;
1821     PetscCall(DMPlexCreateRankField(dm, &ranks));
1822     PetscCall(VecView(ranks, viewer));
1823     PetscCall(VecDestroy(&ranks));
1824   }
1825   /* Optionally view a label */
1826   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1827   if (flg) {
1828     DMLabel label;
1829     Vec     val;
1830 
1831     PetscCall(DMGetLabel(dm, name, &label));
1832     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1833     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1834     PetscCall(VecView(val, viewer));
1835     PetscCall(VecDestroy(&val));
1836   }
1837   PetscFunctionReturn(PETSC_SUCCESS);
1838 }
1839 
1840 /*@
1841   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1842 
1843   Collective
1844 
1845   Input Parameters:
1846 + dm     - The `DM` whose topology is to be saved
1847 - viewer - The `PetscViewer` to save it in
1848 
1849   Level: advanced
1850 
1851 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1852 @*/
1853 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1854 {
1855   PetscBool ishdf5;
1856 
1857   PetscFunctionBegin;
1858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1859   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1860   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1861   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1862   if (ishdf5) {
1863 #if defined(PETSC_HAVE_HDF5)
1864     PetscViewerFormat format;
1865     PetscCall(PetscViewerGetFormat(viewer, &format));
1866     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1867       IS globalPointNumbering;
1868 
1869       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1870       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1871       PetscCall(ISDestroy(&globalPointNumbering));
1872     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1873 #else
1874     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1875 #endif
1876   }
1877   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1878   PetscFunctionReturn(PETSC_SUCCESS);
1879 }
1880 
1881 /*@
1882   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1883 
1884   Collective
1885 
1886   Input Parameters:
1887 + dm     - The `DM` whose coordinates are to be saved
1888 - viewer - The `PetscViewer` for saving
1889 
1890   Level: advanced
1891 
1892 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1893 @*/
1894 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1895 {
1896   PetscBool ishdf5;
1897 
1898   PetscFunctionBegin;
1899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1900   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1901   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1902   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1903   if (ishdf5) {
1904 #if defined(PETSC_HAVE_HDF5)
1905     PetscViewerFormat format;
1906     PetscCall(PetscViewerGetFormat(viewer, &format));
1907     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1908       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1909     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1910 #else
1911     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1912 #endif
1913   }
1914   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1915   PetscFunctionReturn(PETSC_SUCCESS);
1916 }
1917 
1918 /*@
1919   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1920 
1921   Collective
1922 
1923   Input Parameters:
1924 + dm     - The `DM` whose labels are to be saved
1925 - viewer - The `PetscViewer` for saving
1926 
1927   Level: advanced
1928 
1929 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1930 @*/
1931 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1932 {
1933   PetscBool ishdf5;
1934 
1935   PetscFunctionBegin;
1936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1937   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1938   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1939   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1940   if (ishdf5) {
1941 #if defined(PETSC_HAVE_HDF5)
1942     IS                globalPointNumbering;
1943     PetscViewerFormat format;
1944 
1945     PetscCall(PetscViewerGetFormat(viewer, &format));
1946     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1947       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1948       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1949       PetscCall(ISDestroy(&globalPointNumbering));
1950     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1951 #else
1952     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1953 #endif
1954   }
1955   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1956   PetscFunctionReturn(PETSC_SUCCESS);
1957 }
1958 
1959 /*@
1960   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1961 
1962   Collective
1963 
1964   Input Parameters:
1965 + dm        - The `DM` that contains the topology on which the section to be saved is defined
1966 . viewer    - The `PetscViewer` for saving
1967 - sectiondm - The `DM` that contains the section to be saved
1968 
1969   Level: advanced
1970 
1971   Notes:
1972   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.
1973 
1974   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.
1975 
1976 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1977 @*/
1978 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1979 {
1980   PetscBool ishdf5;
1981 
1982   PetscFunctionBegin;
1983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1984   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1985   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1986   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1987   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1988   if (ishdf5) {
1989 #if defined(PETSC_HAVE_HDF5)
1990     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1991 #else
1992     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1993 #endif
1994   }
1995   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1996   PetscFunctionReturn(PETSC_SUCCESS);
1997 }
1998 
1999 /*@
2000   DMPlexGlobalVectorView - Saves a global vector
2001 
2002   Collective
2003 
2004   Input Parameters:
2005 + dm        - The `DM` that represents the topology
2006 . viewer    - The `PetscViewer` to save data with
2007 . sectiondm - The `DM` that contains the global section on which vec is defined
2008 - vec       - The global vector to be saved
2009 
2010   Level: advanced
2011 
2012   Notes:
2013   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.
2014 
2015   Calling sequence:
2016 .vb
2017        DMCreate(PETSC_COMM_WORLD, &dm);
2018        DMSetType(dm, DMPLEX);
2019        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2020        DMClone(dm, &sectiondm);
2021        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2022        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2023        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2024        PetscSectionSetChart(section, pStart, pEnd);
2025        PetscSectionSetUp(section);
2026        DMSetLocalSection(sectiondm, section);
2027        PetscSectionDestroy(&section);
2028        DMGetGlobalVector(sectiondm, &vec);
2029        PetscObjectSetName((PetscObject)vec, "vec_name");
2030        DMPlexTopologyView(dm, viewer);
2031        DMPlexSectionView(dm, viewer, sectiondm);
2032        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2033        DMRestoreGlobalVector(sectiondm, &vec);
2034        DMDestroy(&sectiondm);
2035        DMDestroy(&dm);
2036 .ve
2037 
2038 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection section;
2052     PetscBool    includesConstraints;
2053     PetscInt     m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetGlobalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2072   PetscFunctionReturn(PETSC_SUCCESS);
2073 }
2074 
2075 /*@
2076   DMPlexLocalVectorView - Saves a local vector
2077 
2078   Collective
2079 
2080   Input Parameters:
2081 + dm        - The `DM` that represents the topology
2082 . viewer    - The `PetscViewer` to save data with
2083 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2084 - vec       - The local vector to be saved
2085 
2086   Level: advanced
2087 
2088   Note:
2089   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.
2090 
2091   Calling sequence:
2092 .vb
2093        DMCreate(PETSC_COMM_WORLD, &dm);
2094        DMSetType(dm, DMPLEX);
2095        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2096        DMClone(dm, &sectiondm);
2097        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2098        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2099        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2100        PetscSectionSetChart(section, pStart, pEnd);
2101        PetscSectionSetUp(section);
2102        DMSetLocalSection(sectiondm, section);
2103        DMGetLocalVector(sectiondm, &vec);
2104        PetscObjectSetName((PetscObject)vec, "vec_name");
2105        DMPlexTopologyView(dm, viewer);
2106        DMPlexSectionView(dm, viewer, sectiondm);
2107        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2108        DMRestoreLocalVector(sectiondm, &vec);
2109        DMDestroy(&sectiondm);
2110        DMDestroy(&dm);
2111 .ve
2112 
2113 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2114 @*/
2115 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2116 {
2117   PetscBool ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2123   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2124   /* Check consistency */
2125   {
2126     PetscSection section;
2127     PetscBool    includesConstraints;
2128     PetscInt     m, m1;
2129 
2130     PetscCall(VecGetLocalSize(vec, &m1));
2131     PetscCall(DMGetLocalSection(sectiondm, &section));
2132     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2133     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2134     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2135     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2136   }
2137   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2138   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2139   if (ishdf5) {
2140 #if defined(PETSC_HAVE_HDF5)
2141     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2142 #else
2143     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2144 #endif
2145   }
2146   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2147   PetscFunctionReturn(PETSC_SUCCESS);
2148 }
2149 
2150 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2151 {
2152   PetscBool ishdf5;
2153 
2154   PetscFunctionBegin;
2155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2156   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2157   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2158   if (ishdf5) {
2159 #if defined(PETSC_HAVE_HDF5)
2160     PetscViewerFormat format;
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2163       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2164     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2165       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2166     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2167     PetscFunctionReturn(PETSC_SUCCESS);
2168 #else
2169     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2170 #endif
2171   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2172 }
2173 
2174 /*@
2175   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2176 
2177   Collective
2178 
2179   Input Parameters:
2180 + dm     - The `DM` into which the topology is loaded
2181 - viewer - The `PetscViewer` for the saved topology
2182 
2183   Output Parameter:
2184 . 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
2185 
2186   Level: advanced
2187 
2188 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2189           `PetscViewer`, `PetscSF`
2190 @*/
2191 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2192 {
2193   PetscBool ishdf5;
2194 
2195   PetscFunctionBegin;
2196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2197   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2198   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2199   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2200   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2201   if (ishdf5) {
2202 #if defined(PETSC_HAVE_HDF5)
2203     PetscViewerFormat format;
2204     PetscCall(PetscViewerGetFormat(viewer, &format));
2205     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2206       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2207     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2208 #else
2209     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2210 #endif
2211   }
2212   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2213   PetscFunctionReturn(PETSC_SUCCESS);
2214 }
2215 
2216 /*@
2217   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2218 
2219   Collective
2220 
2221   Input Parameters:
2222 + dm                   - The `DM` into which the coordinates are loaded
2223 . viewer               - The `PetscViewer` for the saved coordinates
2224 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2225 
2226   Level: advanced
2227 
2228 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2229           `PetscSF`, `PetscViewer`
2230 @*/
2231 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2232 {
2233   PetscBool ishdf5;
2234 
2235   PetscFunctionBegin;
2236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2237   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2238   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2239   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2240   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2241   if (ishdf5) {
2242 #if defined(PETSC_HAVE_HDF5)
2243     PetscViewerFormat format;
2244     PetscCall(PetscViewerGetFormat(viewer, &format));
2245     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2246       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2247     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2248 #else
2249     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2250 #endif
2251   }
2252   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2253   PetscFunctionReturn(PETSC_SUCCESS);
2254 }
2255 
2256 /*@
2257   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2258 
2259   Collective
2260 
2261   Input Parameters:
2262 + dm                   - The `DM` into which the labels are loaded
2263 . viewer               - The `PetscViewer` for the saved labels
2264 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2265 
2266   Level: advanced
2267 
2268   Note:
2269   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2270 
2271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2272           `PetscSF`, `PetscViewer`
2273 @*/
2274 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2275 {
2276   PetscBool ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscViewerFormat format;
2287 
2288     PetscCall(PetscViewerGetFormat(viewer, &format));
2289     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2290       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2291     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2292 #else
2293     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2294 #endif
2295   }
2296   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2297   PetscFunctionReturn(PETSC_SUCCESS);
2298 }
2299 
2300 /*@
2301   DMPlexSectionLoad - Loads section into a `DMPLEX`
2302 
2303   Collective
2304 
2305   Input Parameters:
2306 + dm                   - The `DM` that represents the topology
2307 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2308 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2309 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2310 
2311   Output Parameters:
2312 + 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)
2313 - 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)
2314 
2315   Level: advanced
2316 
2317   Notes:
2318   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.
2319 
2320   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.
2321 
2322   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.
2323 
2324   Example using 2 processes:
2325 .vb
2326   NX (number of points on dm): 4
2327   sectionA                   : the on-disk section
2328   vecA                       : a vector associated with sectionA
2329   sectionB                   : sectiondm's local section constructed in this function
2330   vecB (local)               : a vector associated with sectiondm's local section
2331   vecB (global)              : a vector associated with sectiondm's global section
2332 
2333                                      rank 0    rank 1
2334   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2335   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2336   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2337   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2338   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2339   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2340   sectionB->atlasDof             :     1 0 1 | 1 3
2341   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2342   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2343   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2344 .ve
2345   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2346 
2347 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2348 @*/
2349 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2350 {
2351   PetscBool ishdf5;
2352 
2353   PetscFunctionBegin;
2354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2355   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2356   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2357   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2358   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2359   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2370   PetscFunctionReturn(PETSC_SUCCESS);
2371 }
2372 
2373 /*@
2374   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2375 
2376   Collective
2377 
2378   Input Parameters:
2379 + dm        - The `DM` that represents the topology
2380 . viewer    - The `PetscViewer` that represents the on-disk vector data
2381 . sectiondm - The `DM` that contains the global section on which vec is defined
2382 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2383 - vec       - The global vector to set values of
2384 
2385   Level: advanced
2386 
2387   Notes:
2388   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.
2389 
2390   Calling sequence:
2391 .vb
2392        DMCreate(PETSC_COMM_WORLD, &dm);
2393        DMSetType(dm, DMPLEX);
2394        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2395        DMPlexTopologyLoad(dm, viewer, &sfX);
2396        DMClone(dm, &sectiondm);
2397        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2398        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2399        DMGetGlobalVector(sectiondm, &vec);
2400        PetscObjectSetName((PetscObject)vec, "vec_name");
2401        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2402        DMRestoreGlobalVector(sectiondm, &vec);
2403        PetscSFDestroy(&gsf);
2404        PetscSFDestroy(&sfX);
2405        DMDestroy(&sectiondm);
2406        DMDestroy(&dm);
2407 .ve
2408 
2409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2410           `PetscSF`, `PetscViewer`
2411 @*/
2412 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2413 {
2414   PetscBool ishdf5;
2415 
2416   PetscFunctionBegin;
2417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2418   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2419   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2420   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2421   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2422   /* Check consistency */
2423   {
2424     PetscSection section;
2425     PetscBool    includesConstraints;
2426     PetscInt     m, m1;
2427 
2428     PetscCall(VecGetLocalSize(vec, &m1));
2429     PetscCall(DMGetGlobalSection(sectiondm, &section));
2430     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2431     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2432     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2433     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2434   }
2435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2436   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2437   if (ishdf5) {
2438 #if defined(PETSC_HAVE_HDF5)
2439     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2440 #else
2441     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2442 #endif
2443   }
2444   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2445   PetscFunctionReturn(PETSC_SUCCESS);
2446 }
2447 
2448 /*@
2449   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2450 
2451   Collective
2452 
2453   Input Parameters:
2454 + dm        - The `DM` that represents the topology
2455 . viewer    - The `PetscViewer` that represents the on-disk vector data
2456 . sectiondm - The `DM` that contains the local section on which vec is defined
2457 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2458 - vec       - The local vector to set values of
2459 
2460   Level: advanced
2461 
2462   Notes:
2463   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.
2464 
2465   Calling sequence:
2466 .vb
2467        DMCreate(PETSC_COMM_WORLD, &dm);
2468        DMSetType(dm, DMPLEX);
2469        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2470        DMPlexTopologyLoad(dm, viewer, &sfX);
2471        DMClone(dm, &sectiondm);
2472        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2473        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2474        DMGetLocalVector(sectiondm, &vec);
2475        PetscObjectSetName((PetscObject)vec, "vec_name");
2476        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2477        DMRestoreLocalVector(sectiondm, &vec);
2478        PetscSFDestroy(&lsf);
2479        PetscSFDestroy(&sfX);
2480        DMDestroy(&sectiondm);
2481        DMDestroy(&dm);
2482 .ve
2483 
2484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2485           `PetscSF`, `PetscViewer`
2486 @*/
2487 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2488 {
2489   PetscBool ishdf5;
2490 
2491   PetscFunctionBegin;
2492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2493   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2494   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2495   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2496   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2497   /* Check consistency */
2498   {
2499     PetscSection section;
2500     PetscBool    includesConstraints;
2501     PetscInt     m, m1;
2502 
2503     PetscCall(VecGetLocalSize(vec, &m1));
2504     PetscCall(DMGetLocalSection(sectiondm, &section));
2505     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2506     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2507     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2508     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2509   }
2510   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2511   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2512   if (ishdf5) {
2513 #if defined(PETSC_HAVE_HDF5)
2514     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2515 #else
2516     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2517 #endif
2518   }
2519   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2520   PetscFunctionReturn(PETSC_SUCCESS);
2521 }
2522 
2523 PetscErrorCode DMDestroy_Plex(DM dm)
2524 {
2525   DM_Plex *mesh = (DM_Plex *)dm->data;
2526 
2527   PetscFunctionBegin;
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2529   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2530   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2531   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2532   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2533   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2534   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2535   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2536   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2537   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2538   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2539   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2540   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2541   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2542   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2543   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2544   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2545   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2546   PetscCall(PetscFree(mesh->cones));
2547   PetscCall(PetscFree(mesh->coneOrientations));
2548   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2549   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2550   PetscCall(PetscFree(mesh->supports));
2551   PetscCall(PetscFree(mesh->cellTypes));
2552   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2553   PetscCall(PetscFree(mesh->tetgenOpts));
2554   PetscCall(PetscFree(mesh->triangleOpts));
2555   PetscCall(PetscFree(mesh->transformType));
2556   PetscCall(PetscFree(mesh->distributionName));
2557   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2558   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2559   PetscCall(ISDestroy(&mesh->subpointIS));
2560   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2561   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2562   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2563   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2564   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2565   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2566   PetscCall(ISDestroy(&mesh->anchorIS));
2567   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2568   PetscCall(PetscFree(mesh->parents));
2569   PetscCall(PetscFree(mesh->childIDs));
2570   PetscCall(PetscSectionDestroy(&mesh->childSection));
2571   PetscCall(PetscFree(mesh->children));
2572   PetscCall(DMDestroy(&mesh->referenceTree));
2573   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2574   PetscCall(PetscFree(mesh->neighbors));
2575   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2576   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2577   PetscCall(PetscFree(mesh));
2578   PetscFunctionReturn(PETSC_SUCCESS);
2579 }
2580 
2581 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2582 {
2583   PetscSection           sectionGlobal;
2584   PetscInt               bs = -1, mbs;
2585   PetscInt               localSize, localStart = 0;
2586   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2587   MatType                mtype;
2588   ISLocalToGlobalMapping ltog;
2589 
2590   PetscFunctionBegin;
2591   PetscCall(MatInitializePackage());
2592   mtype = dm->mattype;
2593   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2594   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2595   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2596   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2597   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2598   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2599   PetscCall(MatSetType(*J, mtype));
2600   PetscCall(MatSetFromOptions(*J));
2601   PetscCall(MatGetBlockSize(*J, &mbs));
2602   if (mbs > 1) bs = mbs;
2603   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2604   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2605   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2606   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2607   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2608   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2609   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2610   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2611   if (!isShell) {
2612     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2613     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2614     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2615 
2616     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2617 
2618     PetscCall(PetscCalloc1(localSize, &pblocks));
2619     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2620     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2621     for (p = pStart; p < pEnd; ++p) {
2622       switch (dm->blocking_type) {
2623       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2624         PetscInt bdof, offset;
2625 
2626         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2627         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2628         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2629         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2630         dof  = dof < 0 ? -(dof + 1) : dof;
2631         bdof = cdof && (dof - cdof) ? 1 : dof;
2632         if (dof) {
2633           if (bs < 0) {
2634             bs = bdof;
2635           } else if (bs != bdof) {
2636             bs = 1;
2637           }
2638         }
2639       } break;
2640       case DM_BLOCKING_FIELD_NODE: {
2641         for (PetscInt field = 0; field < num_fields; field++) {
2642           PetscInt num_comp, bdof, offset;
2643           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2644           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2645           if (dof < 0) continue;
2646           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2647           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2648           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);
2649           PetscInt num_nodes = dof / num_comp;
2650           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2651           // Handle possibly constant block size (unlikely)
2652           bdof = cdof && (dof - cdof) ? 1 : dof;
2653           if (dof) {
2654             if (bs < 0) {
2655               bs = bdof;
2656             } else if (bs != bdof) {
2657               bs = 1;
2658             }
2659           }
2660         }
2661       } break;
2662       }
2663     }
2664     /* Must have same blocksize on all procs (some might have no points) */
2665     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2666     bsLocal[1] = bs;
2667     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2668     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2669     else bs = bsMinMax[0];
2670     bs = PetscMax(1, bs);
2671     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2672     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2673       PetscCall(MatSetBlockSize(*J, bs));
2674       PetscCall(MatSetUp(*J));
2675     } else {
2676       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2677       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2678       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2679     }
2680     { // Consolidate blocks
2681       PetscInt nblocks = 0;
2682       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2683         if (pblocks[i] == 0) continue;
2684         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2685         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]);
2686       }
2687       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2688     }
2689     PetscCall(PetscFree(pblocks));
2690   }
2691   PetscCall(MatSetDM(*J, dm));
2692   PetscFunctionReturn(PETSC_SUCCESS);
2693 }
2694 
2695 /*@
2696   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2697 
2698   Not Collective
2699 
2700   Input Parameter:
2701 . dm - The `DMPLEX`
2702 
2703   Output Parameter:
2704 . subsection - The subdomain section
2705 
2706   Level: developer
2707 
2708 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2709 @*/
2710 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2711 {
2712   DM_Plex *mesh = (DM_Plex *)dm->data;
2713 
2714   PetscFunctionBegin;
2715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2716   if (!mesh->subdomainSection) {
2717     PetscSection section;
2718     PetscSF      sf;
2719 
2720     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2721     PetscCall(DMGetLocalSection(dm, &section));
2722     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2723     PetscCall(PetscSFDestroy(&sf));
2724   }
2725   *subsection = mesh->subdomainSection;
2726   PetscFunctionReturn(PETSC_SUCCESS);
2727 }
2728 
2729 /*@
2730   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2731 
2732   Not Collective
2733 
2734   Input Parameter:
2735 . dm - The `DMPLEX`
2736 
2737   Output Parameters:
2738 + pStart - The first mesh point
2739 - pEnd   - The upper bound for mesh points
2740 
2741   Level: beginner
2742 
2743 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2744 @*/
2745 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2746 {
2747   DM_Plex *mesh = (DM_Plex *)dm->data;
2748 
2749   PetscFunctionBegin;
2750   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2751   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2752   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2753   PetscFunctionReturn(PETSC_SUCCESS);
2754 }
2755 
2756 /*@
2757   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2758 
2759   Not Collective
2760 
2761   Input Parameters:
2762 + dm     - The `DMPLEX`
2763 . pStart - The first mesh point
2764 - pEnd   - The upper bound for mesh points
2765 
2766   Level: beginner
2767 
2768 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2769 @*/
2770 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2771 {
2772   DM_Plex *mesh = (DM_Plex *)dm->data;
2773 
2774   PetscFunctionBegin;
2775   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2776   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2777   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2778   PetscCall(PetscFree(mesh->cellTypes));
2779   PetscFunctionReturn(PETSC_SUCCESS);
2780 }
2781 
2782 /*@
2783   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2784 
2785   Not Collective
2786 
2787   Input Parameters:
2788 + dm - The `DMPLEX`
2789 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2790 
2791   Output Parameter:
2792 . size - The cone size for point `p`
2793 
2794   Level: beginner
2795 
2796 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2797 @*/
2798 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2799 {
2800   DM_Plex *mesh = (DM_Plex *)dm->data;
2801 
2802   PetscFunctionBegin;
2803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2804   PetscAssertPointer(size, 3);
2805   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2806   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2807   PetscFunctionReturn(PETSC_SUCCESS);
2808 }
2809 
2810 /*@
2811   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2812 
2813   Not Collective
2814 
2815   Input Parameters:
2816 + dm   - The `DMPLEX`
2817 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2818 - size - The cone size for point `p`
2819 
2820   Level: beginner
2821 
2822   Note:
2823   This should be called after `DMPlexSetChart()`.
2824 
2825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2826 @*/
2827 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2828 {
2829   DM_Plex *mesh = (DM_Plex *)dm->data;
2830 
2831   PetscFunctionBegin;
2832   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2833   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2834   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2835   PetscFunctionReturn(PETSC_SUCCESS);
2836 }
2837 
2838 /*@C
2839   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2840 
2841   Not Collective
2842 
2843   Input Parameters:
2844 + dm - The `DMPLEX`
2845 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2846 
2847   Output Parameter:
2848 . cone - An array of points which are on the in-edges for point `p`
2849 
2850   Level: beginner
2851 
2852   Fortran Notes:
2853   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2854   `DMPlexRestoreCone()` is not needed/available in C.
2855 
2856 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2857 @*/
2858 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2859 {
2860   DM_Plex *mesh = (DM_Plex *)dm->data;
2861   PetscInt off;
2862 
2863   PetscFunctionBegin;
2864   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2865   PetscAssertPointer(cone, 3);
2866   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2867   *cone = &mesh->cones[off];
2868   PetscFunctionReturn(PETSC_SUCCESS);
2869 }
2870 
2871 /*@C
2872   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2873 
2874   Not Collective
2875 
2876   Input Parameters:
2877 + dm - The `DMPLEX`
2878 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2879 
2880   Output Parameters:
2881 + pConesSection - `PetscSection` describing the layout of `pCones`
2882 - pCones        - An array of points which are on the in-edges for the point set `p`
2883 
2884   Level: intermediate
2885 
2886 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2887 @*/
2888 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2889 {
2890   PetscSection cs, newcs;
2891   PetscInt    *cones;
2892   PetscInt    *newarr = NULL;
2893   PetscInt     n;
2894 
2895   PetscFunctionBegin;
2896   PetscCall(DMPlexGetCones(dm, &cones));
2897   PetscCall(DMPlexGetConeSection(dm, &cs));
2898   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2899   if (pConesSection) *pConesSection = newcs;
2900   if (pCones) {
2901     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2902     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2903   }
2904   PetscFunctionReturn(PETSC_SUCCESS);
2905 }
2906 
2907 /*@
2908   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2909 
2910   Not Collective
2911 
2912   Input Parameters:
2913 + dm     - The `DMPLEX`
2914 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2915 
2916   Output Parameter:
2917 . expandedPoints - An array of vertices recursively expanded from input points
2918 
2919   Level: advanced
2920 
2921   Notes:
2922   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2923 
2924   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2925 
2926 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2927           `DMPlexGetDepth()`, `IS`
2928 @*/
2929 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2930 {
2931   IS      *expandedPointsAll;
2932   PetscInt depth;
2933 
2934   PetscFunctionBegin;
2935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2936   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2937   PetscAssertPointer(expandedPoints, 3);
2938   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2939   *expandedPoints = expandedPointsAll[0];
2940   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2941   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2942   PetscFunctionReturn(PETSC_SUCCESS);
2943 }
2944 
2945 /*@
2946   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).
2947 
2948   Not Collective
2949 
2950   Input Parameters:
2951 + dm     - The `DMPLEX`
2952 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2953 
2954   Output Parameters:
2955 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2956 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2957 - sections       - (optional) An array of sections which describe mappings from points to their cone points
2958 
2959   Level: advanced
2960 
2961   Notes:
2962   Like `DMPlexGetConeTuple()` but recursive.
2963 
2964   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.
2965   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2966 
2967   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\:
2968   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2969   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2970 
2971 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2972           `DMPlexGetDepth()`, `PetscSection`, `IS`
2973 @*/
2974 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2975 {
2976   const PetscInt *arr0 = NULL, *cone = NULL;
2977   PetscInt       *arr = NULL, *newarr = NULL;
2978   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2979   IS             *expandedPoints_;
2980   PetscSection   *sections_;
2981 
2982   PetscFunctionBegin;
2983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2984   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2985   if (depth) PetscAssertPointer(depth, 3);
2986   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
2987   if (sections) PetscAssertPointer(sections, 5);
2988   PetscCall(ISGetLocalSize(points, &n));
2989   PetscCall(ISGetIndices(points, &arr0));
2990   PetscCall(DMPlexGetDepth(dm, &depth_));
2991   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2992   PetscCall(PetscCalloc1(depth_, &sections_));
2993   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2994   for (d = depth_ - 1; d >= 0; d--) {
2995     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2996     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2997     for (i = 0; i < n; i++) {
2998       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2999       if (arr[i] >= start && arr[i] < end) {
3000         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3001         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3002       } else {
3003         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3004       }
3005     }
3006     PetscCall(PetscSectionSetUp(sections_[d]));
3007     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3008     PetscCall(PetscMalloc1(newn, &newarr));
3009     for (i = 0; i < n; i++) {
3010       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3011       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3012       if (cn > 1) {
3013         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3014         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3015       } else {
3016         newarr[co] = arr[i];
3017       }
3018     }
3019     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3020     arr = newarr;
3021     n   = newn;
3022   }
3023   PetscCall(ISRestoreIndices(points, &arr0));
3024   *depth = depth_;
3025   if (expandedPoints) *expandedPoints = expandedPoints_;
3026   else {
3027     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3028     PetscCall(PetscFree(expandedPoints_));
3029   }
3030   if (sections) *sections = sections_;
3031   else {
3032     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3033     PetscCall(PetscFree(sections_));
3034   }
3035   PetscFunctionReturn(PETSC_SUCCESS);
3036 }
3037 
3038 /*@
3039   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3040 
3041   Not Collective
3042 
3043   Input Parameters:
3044 + dm     - The `DMPLEX`
3045 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3046 
3047   Output Parameters:
3048 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3049 . expandedPoints - (optional) An array of recursively expanded cones
3050 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3051 
3052   Level: advanced
3053 
3054   Note:
3055   See `DMPlexGetConeRecursive()`
3056 
3057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3058           `DMPlexGetDepth()`, `IS`, `PetscSection`
3059 @*/
3060 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3061 {
3062   PetscInt d, depth_;
3063 
3064   PetscFunctionBegin;
3065   PetscCall(DMPlexGetDepth(dm, &depth_));
3066   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3067   if (depth) *depth = 0;
3068   if (expandedPoints) {
3069     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3070     PetscCall(PetscFree(*expandedPoints));
3071   }
3072   if (sections) {
3073     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3074     PetscCall(PetscFree(*sections));
3075   }
3076   PetscFunctionReturn(PETSC_SUCCESS);
3077 }
3078 
3079 /*@
3080   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
3081 
3082   Not Collective
3083 
3084   Input Parameters:
3085 + dm   - The `DMPLEX`
3086 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3087 - cone - An array of points which are on the in-edges for point `p`
3088 
3089   Level: beginner
3090 
3091   Note:
3092   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3093 
3094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3095 @*/
3096 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3097 {
3098   DM_Plex *mesh = (DM_Plex *)dm->data;
3099   PetscInt dof, off, c;
3100 
3101   PetscFunctionBegin;
3102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3103   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3104   if (dof) PetscAssertPointer(cone, 3);
3105   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3106   if (PetscDefined(USE_DEBUG)) {
3107     PetscInt pStart, pEnd;
3108     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3109     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);
3110     for (c = 0; c < dof; ++c) {
3111       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);
3112       mesh->cones[off + c] = cone[c];
3113     }
3114   } else {
3115     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3116   }
3117   PetscFunctionReturn(PETSC_SUCCESS);
3118 }
3119 
3120 /*@C
3121   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3122 
3123   Not Collective
3124 
3125   Input Parameters:
3126 + dm - The `DMPLEX`
3127 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3128 
3129   Output Parameter:
3130 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3131                     integer giving the prescription for cone traversal.
3132 
3133   Level: beginner
3134 
3135   Note:
3136   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3137   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3138   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3139   with the identity.
3140 
3141   Fortran Notes:
3142   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3143   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3144 
3145 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3146 @*/
3147 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3148 {
3149   DM_Plex *mesh = (DM_Plex *)dm->data;
3150   PetscInt off;
3151 
3152   PetscFunctionBegin;
3153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3154   if (PetscDefined(USE_DEBUG)) {
3155     PetscInt dof;
3156     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3157     if (dof) PetscAssertPointer(coneOrientation, 3);
3158   }
3159   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3160 
3161   *coneOrientation = &mesh->coneOrientations[off];
3162   PetscFunctionReturn(PETSC_SUCCESS);
3163 }
3164 
3165 /*@
3166   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3167 
3168   Not Collective
3169 
3170   Input Parameters:
3171 + dm              - The `DMPLEX`
3172 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3173 - coneOrientation - An array of orientations
3174 
3175   Level: beginner
3176 
3177   Notes:
3178   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3179 
3180   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3181 
3182 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3183 @*/
3184 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3185 {
3186   DM_Plex *mesh = (DM_Plex *)dm->data;
3187   PetscInt pStart, pEnd;
3188   PetscInt dof, off, c;
3189 
3190   PetscFunctionBegin;
3191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3192   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3193   if (dof) PetscAssertPointer(coneOrientation, 3);
3194   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3195   if (PetscDefined(USE_DEBUG)) {
3196     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3197     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);
3198     for (c = 0; c < dof; ++c) {
3199       PetscInt cdof, o = coneOrientation[c];
3200 
3201       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3202       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);
3203       mesh->coneOrientations[off + c] = o;
3204     }
3205   } else {
3206     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3207   }
3208   PetscFunctionReturn(PETSC_SUCCESS);
3209 }
3210 
3211 /*@
3212   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3213 
3214   Not Collective
3215 
3216   Input Parameters:
3217 + dm        - The `DMPLEX`
3218 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3219 . conePos   - The local index in the cone where the point should be put
3220 - conePoint - The mesh point to insert
3221 
3222   Level: beginner
3223 
3224 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3225 @*/
3226 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3227 {
3228   DM_Plex *mesh = (DM_Plex *)dm->data;
3229   PetscInt pStart, pEnd;
3230   PetscInt dof, off;
3231 
3232   PetscFunctionBegin;
3233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3234   if (PetscDefined(USE_DEBUG)) {
3235     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3236     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);
3237     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);
3238     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3239     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);
3240   }
3241   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3242   mesh->cones[off + conePos] = conePoint;
3243   PetscFunctionReturn(PETSC_SUCCESS);
3244 }
3245 
3246 /*@
3247   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3248 
3249   Not Collective
3250 
3251   Input Parameters:
3252 + dm              - The `DMPLEX`
3253 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3254 . conePos         - The local index in the cone where the point should be put
3255 - coneOrientation - The point orientation to insert
3256 
3257   Level: beginner
3258 
3259   Note:
3260   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3261 
3262 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3263 @*/
3264 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3265 {
3266   DM_Plex *mesh = (DM_Plex *)dm->data;
3267   PetscInt pStart, pEnd;
3268   PetscInt dof, off;
3269 
3270   PetscFunctionBegin;
3271   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3272   if (PetscDefined(USE_DEBUG)) {
3273     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3274     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);
3275     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3276     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);
3277   }
3278   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3279   mesh->coneOrientations[off + conePos] = coneOrientation;
3280   PetscFunctionReturn(PETSC_SUCCESS);
3281 }
3282 
3283 /*@C
3284   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3285 
3286   Not collective
3287 
3288   Input Parameters:
3289 + dm - The DMPlex
3290 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3291 
3292   Output Parameters:
3293 + cone - An array of points which are on the in-edges for point `p`
3294 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3295         integer giving the prescription for cone traversal.
3296 
3297   Level: beginner
3298 
3299   Notes:
3300   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3301   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3302   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3303   with the identity.
3304 
3305   Fortran Notes:
3306   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3307   `DMPlexRestoreCone()` is not needed/available in C.
3308 
3309 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3310 @*/
3311 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3312 {
3313   DM_Plex *mesh = (DM_Plex *)dm->data;
3314 
3315   PetscFunctionBegin;
3316   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3317   if (mesh->tr) {
3318     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3319   } else {
3320     PetscInt off;
3321     if (PetscDefined(USE_DEBUG)) {
3322       PetscInt dof;
3323       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3324       if (dof) {
3325         if (cone) PetscAssertPointer(cone, 3);
3326         if (ornt) PetscAssertPointer(ornt, 4);
3327       }
3328     }
3329     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3330     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3331     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3332   }
3333   PetscFunctionReturn(PETSC_SUCCESS);
3334 }
3335 
3336 /*@C
3337   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3338 
3339   Not Collective
3340 
3341   Input Parameters:
3342 + dm   - The DMPlex
3343 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3344 . cone - An array of points which are on the in-edges for point p
3345 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3346         integer giving the prescription for cone traversal.
3347 
3348   Level: beginner
3349 
3350   Notes:
3351   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3352   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3353   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3354   with the identity.
3355 
3356   Fortran Notes:
3357   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3358   `DMPlexRestoreCone()` is not needed/available in C.
3359 
3360 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3361 @*/
3362 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3363 {
3364   DM_Plex *mesh = (DM_Plex *)dm->data;
3365 
3366   PetscFunctionBegin;
3367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3368   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3369   PetscFunctionReturn(PETSC_SUCCESS);
3370 }
3371 
3372 /*@
3373   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3374 
3375   Not Collective
3376 
3377   Input Parameters:
3378 + dm - The `DMPLEX`
3379 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3380 
3381   Output Parameter:
3382 . size - The support size for point `p`
3383 
3384   Level: beginner
3385 
3386 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3387 @*/
3388 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3389 {
3390   DM_Plex *mesh = (DM_Plex *)dm->data;
3391 
3392   PetscFunctionBegin;
3393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3394   PetscAssertPointer(size, 3);
3395   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3396   PetscFunctionReturn(PETSC_SUCCESS);
3397 }
3398 
3399 /*@
3400   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3401 
3402   Not Collective
3403 
3404   Input Parameters:
3405 + dm   - The `DMPLEX`
3406 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3407 - size - The support size for point `p`
3408 
3409   Level: beginner
3410 
3411   Note:
3412   This should be called after `DMPlexSetChart()`.
3413 
3414 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3415 @*/
3416 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3417 {
3418   DM_Plex *mesh = (DM_Plex *)dm->data;
3419 
3420   PetscFunctionBegin;
3421   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3422   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3423   PetscFunctionReturn(PETSC_SUCCESS);
3424 }
3425 
3426 /*@C
3427   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3428 
3429   Not Collective
3430 
3431   Input Parameters:
3432 + dm - The `DMPLEX`
3433 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3434 
3435   Output Parameter:
3436 . support - An array of points which are on the out-edges for point `p`
3437 
3438   Level: beginner
3439 
3440   Fortran Notes:
3441   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3442   `DMPlexRestoreSupport()` is not needed/available in C.
3443 
3444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3445 @*/
3446 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3447 {
3448   DM_Plex *mesh = (DM_Plex *)dm->data;
3449   PetscInt off;
3450 
3451   PetscFunctionBegin;
3452   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3453   PetscAssertPointer(support, 3);
3454   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3455   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3456   PetscFunctionReturn(PETSC_SUCCESS);
3457 }
3458 
3459 /*@
3460   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3461 
3462   Not Collective
3463 
3464   Input Parameters:
3465 + dm      - The `DMPLEX`
3466 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3467 - support - An array of points which are on the out-edges for point `p`
3468 
3469   Level: beginner
3470 
3471   Note:
3472   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3473 
3474 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3475 @*/
3476 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3477 {
3478   DM_Plex *mesh = (DM_Plex *)dm->data;
3479   PetscInt pStart, pEnd;
3480   PetscInt dof, off, c;
3481 
3482   PetscFunctionBegin;
3483   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3484   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3485   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3486   if (dof) PetscAssertPointer(support, 3);
3487   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3488   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);
3489   for (c = 0; c < dof; ++c) {
3490     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);
3491     mesh->supports[off + c] = support[c];
3492   }
3493   PetscFunctionReturn(PETSC_SUCCESS);
3494 }
3495 
3496 /*@
3497   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3498 
3499   Not Collective
3500 
3501   Input Parameters:
3502 + dm           - The `DMPLEX`
3503 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3504 . supportPos   - The local index in the cone where the point should be put
3505 - supportPoint - The mesh point to insert
3506 
3507   Level: beginner
3508 
3509 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3510 @*/
3511 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3512 {
3513   DM_Plex *mesh = (DM_Plex *)dm->data;
3514   PetscInt pStart, pEnd;
3515   PetscInt dof, off;
3516 
3517   PetscFunctionBegin;
3518   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3519   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3520   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3521   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3522   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);
3523   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);
3524   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);
3525   mesh->supports[off + supportPos] = supportPoint;
3526   PetscFunctionReturn(PETSC_SUCCESS);
3527 }
3528 
3529 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3530 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3531 {
3532   switch (ct) {
3533   case DM_POLYTOPE_SEGMENT:
3534     if (o == -1) return -2;
3535     break;
3536   case DM_POLYTOPE_TRIANGLE:
3537     if (o == -3) return -1;
3538     if (o == -2) return -3;
3539     if (o == -1) return -2;
3540     break;
3541   case DM_POLYTOPE_QUADRILATERAL:
3542     if (o == -4) return -2;
3543     if (o == -3) return -1;
3544     if (o == -2) return -4;
3545     if (o == -1) return -3;
3546     break;
3547   default:
3548     return o;
3549   }
3550   return o;
3551 }
3552 
3553 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3554 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3555 {
3556   switch (ct) {
3557   case DM_POLYTOPE_SEGMENT:
3558     if ((o == -2) || (o == 1)) return -1;
3559     if (o == -1) return 0;
3560     break;
3561   case DM_POLYTOPE_TRIANGLE:
3562     if (o == -3) return -2;
3563     if (o == -2) return -1;
3564     if (o == -1) return -3;
3565     break;
3566   case DM_POLYTOPE_QUADRILATERAL:
3567     if (o == -4) return -2;
3568     if (o == -3) return -1;
3569     if (o == -2) return -4;
3570     if (o == -1) return -3;
3571     break;
3572   default:
3573     return o;
3574   }
3575   return o;
3576 }
3577 
3578 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3579 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3580 {
3581   PetscInt pStart, pEnd, p;
3582 
3583   PetscFunctionBegin;
3584   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3585   for (p = pStart; p < pEnd; ++p) {
3586     const PetscInt *cone, *ornt;
3587     PetscInt        coneSize, c;
3588 
3589     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3590     PetscCall(DMPlexGetCone(dm, p, &cone));
3591     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3592     for (c = 0; c < coneSize; ++c) {
3593       DMPolytopeType ct;
3594       const PetscInt o = ornt[c];
3595 
3596       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3597       switch (ct) {
3598       case DM_POLYTOPE_SEGMENT:
3599         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3600         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3601         break;
3602       case DM_POLYTOPE_TRIANGLE:
3603         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3604         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3605         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3606         break;
3607       case DM_POLYTOPE_QUADRILATERAL:
3608         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3609         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3610         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3611         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3612         break;
3613       default:
3614         break;
3615       }
3616     }
3617   }
3618   PetscFunctionReturn(PETSC_SUCCESS);
3619 }
3620 
3621 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3622 {
3623   DM_Plex *mesh = (DM_Plex *)dm->data;
3624 
3625   PetscFunctionBeginHot;
3626   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3627     if (useCone) {
3628       PetscCall(DMPlexGetConeSize(dm, p, size));
3629       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3630     } else {
3631       PetscCall(DMPlexGetSupportSize(dm, p, size));
3632       PetscCall(DMPlexGetSupport(dm, p, arr));
3633     }
3634   } else {
3635     if (useCone) {
3636       const PetscSection s   = mesh->coneSection;
3637       const PetscInt     ps  = p - s->pStart;
3638       const PetscInt     off = s->atlasOff[ps];
3639 
3640       *size = s->atlasDof[ps];
3641       *arr  = mesh->cones + off;
3642       *ornt = mesh->coneOrientations + off;
3643     } else {
3644       const PetscSection s   = mesh->supportSection;
3645       const PetscInt     ps  = p - s->pStart;
3646       const PetscInt     off = s->atlasOff[ps];
3647 
3648       *size = s->atlasDof[ps];
3649       *arr  = mesh->supports + off;
3650     }
3651   }
3652   PetscFunctionReturn(PETSC_SUCCESS);
3653 }
3654 
3655 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3656 {
3657   DM_Plex *mesh = (DM_Plex *)dm->data;
3658 
3659   PetscFunctionBeginHot;
3660   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3661     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3662   }
3663   PetscFunctionReturn(PETSC_SUCCESS);
3664 }
3665 
3666 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3667 {
3668   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3669   PetscInt       *closure;
3670   const PetscInt *tmp = NULL, *tmpO = NULL;
3671   PetscInt        off = 0, tmpSize, t;
3672 
3673   PetscFunctionBeginHot;
3674   if (ornt) {
3675     PetscCall(DMPlexGetCellType(dm, p, &ct));
3676     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3677   }
3678   if (*points) {
3679     closure = *points;
3680   } else {
3681     PetscInt maxConeSize, maxSupportSize;
3682     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3683     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3684   }
3685   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3686   if (ct == DM_POLYTOPE_UNKNOWN) {
3687     closure[off++] = p;
3688     closure[off++] = 0;
3689     for (t = 0; t < tmpSize; ++t) {
3690       closure[off++] = tmp[t];
3691       closure[off++] = tmpO ? tmpO[t] : 0;
3692     }
3693   } else {
3694     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3695 
3696     /* We assume that cells with a valid type have faces with a valid type */
3697     closure[off++] = p;
3698     closure[off++] = ornt;
3699     for (t = 0; t < tmpSize; ++t) {
3700       DMPolytopeType ft;
3701 
3702       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3703       closure[off++] = tmp[arr[t]];
3704       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3705     }
3706   }
3707   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3708   if (numPoints) *numPoints = tmpSize + 1;
3709   if (points) *points = closure;
3710   PetscFunctionReturn(PETSC_SUCCESS);
3711 }
3712 
3713 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3714 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3715 {
3716   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3717   const PetscInt *cone, *ornt;
3718   PetscInt       *pts, *closure = NULL;
3719   DMPolytopeType  ft;
3720   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3721   PetscInt        dim, coneSize, c, d, clSize, cl;
3722 
3723   PetscFunctionBeginHot;
3724   PetscCall(DMGetDimension(dm, &dim));
3725   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3726   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3727   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3728   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3729   maxSize       = PetscMax(coneSeries, supportSeries);
3730   if (*points) {
3731     pts = *points;
3732   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3733   c        = 0;
3734   pts[c++] = point;
3735   pts[c++] = o;
3736   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3737   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3738   for (cl = 0; cl < clSize * 2; cl += 2) {
3739     pts[c++] = closure[cl];
3740     pts[c++] = closure[cl + 1];
3741   }
3742   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3743   for (cl = 0; cl < clSize * 2; cl += 2) {
3744     pts[c++] = closure[cl];
3745     pts[c++] = closure[cl + 1];
3746   }
3747   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3748   for (d = 2; d < coneSize; ++d) {
3749     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3750     pts[c++] = cone[arr[d * 2 + 0]];
3751     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3752   }
3753   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3754   if (dim >= 3) {
3755     for (d = 2; d < coneSize; ++d) {
3756       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3757       const PetscInt *fcone, *fornt;
3758       PetscInt        fconeSize, fc, i;
3759 
3760       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3761       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3762       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3763       for (fc = 0; fc < fconeSize; ++fc) {
3764         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3765         const PetscInt co = farr[fc * 2 + 1];
3766 
3767         for (i = 0; i < c; i += 2)
3768           if (pts[i] == cp) break;
3769         if (i == c) {
3770           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3771           pts[c++] = cp;
3772           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3773         }
3774       }
3775       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3776     }
3777   }
3778   *numPoints = c / 2;
3779   *points    = pts;
3780   PetscFunctionReturn(PETSC_SUCCESS);
3781 }
3782 
3783 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3784 {
3785   DMPolytopeType ct;
3786   PetscInt      *closure, *fifo;
3787   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3788   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3789   PetscInt       depth, maxSize;
3790 
3791   PetscFunctionBeginHot;
3792   PetscCall(DMPlexGetDepth(dm, &depth));
3793   if (depth == 1) {
3794     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3795     PetscFunctionReturn(PETSC_SUCCESS);
3796   }
3797   PetscCall(DMPlexGetCellType(dm, p, &ct));
3798   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3799   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3800     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3801     PetscFunctionReturn(PETSC_SUCCESS);
3802   }
3803   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3804   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3805   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3806   maxSize       = PetscMax(coneSeries, supportSeries);
3807   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3808   if (*points) {
3809     closure = *points;
3810   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3811   closure[closureSize++] = p;
3812   closure[closureSize++] = ornt;
3813   fifo[fifoSize++]       = p;
3814   fifo[fifoSize++]       = ornt;
3815   fifo[fifoSize++]       = ct;
3816   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3817   while (fifoSize - fifoStart) {
3818     const PetscInt       q    = fifo[fifoStart++];
3819     const PetscInt       o    = fifo[fifoStart++];
3820     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3821     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3822     const PetscInt      *tmp, *tmpO = NULL;
3823     PetscInt             tmpSize, t;
3824 
3825     if (PetscDefined(USE_DEBUG)) {
3826       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3827       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);
3828     }
3829     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3830     for (t = 0; t < tmpSize; ++t) {
3831       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3832       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3833       const PetscInt cp = tmp[ip];
3834       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3835       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3836       PetscInt       c;
3837 
3838       /* Check for duplicate */
3839       for (c = 0; c < closureSize; c += 2) {
3840         if (closure[c] == cp) break;
3841       }
3842       if (c == closureSize) {
3843         closure[closureSize++] = cp;
3844         closure[closureSize++] = co;
3845         fifo[fifoSize++]       = cp;
3846         fifo[fifoSize++]       = co;
3847         fifo[fifoSize++]       = ct;
3848       }
3849     }
3850     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3851   }
3852   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3853   if (numPoints) *numPoints = closureSize / 2;
3854   if (points) *points = closure;
3855   PetscFunctionReturn(PETSC_SUCCESS);
3856 }
3857 
3858 /*@C
3859   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3860 
3861   Not Collective
3862 
3863   Input Parameters:
3864 + dm      - The `DMPLEX`
3865 . p       - The mesh point
3866 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3867 
3868   Input/Output Parameter:
3869 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3870            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3871 
3872   Output Parameter:
3873 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3874 
3875   Level: beginner
3876 
3877   Note:
3878   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3879 
3880   Fortran Notes:
3881   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3882 
3883 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3884 @*/
3885 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3886 {
3887   PetscFunctionBeginHot;
3888   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3889   if (numPoints) PetscAssertPointer(numPoints, 4);
3890   if (points) PetscAssertPointer(points, 5);
3891   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3892   PetscFunctionReturn(PETSC_SUCCESS);
3893 }
3894 
3895 /*@C
3896   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3897 
3898   Not Collective
3899 
3900   Input Parameters:
3901 + dm        - The `DMPLEX`
3902 . p         - The mesh point
3903 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3904 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3905 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3906 
3907   Level: beginner
3908 
3909   Note:
3910   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3911 
3912 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3913 @*/
3914 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3915 {
3916   PetscFunctionBeginHot;
3917   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3918   if (numPoints) *numPoints = 0;
3919   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3920   PetscFunctionReturn(PETSC_SUCCESS);
3921 }
3922 
3923 /*@
3924   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3925 
3926   Not Collective
3927 
3928   Input Parameter:
3929 . dm - The `DMPLEX`
3930 
3931   Output Parameters:
3932 + maxConeSize    - The maximum number of in-edges
3933 - maxSupportSize - The maximum number of out-edges
3934 
3935   Level: beginner
3936 
3937 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3938 @*/
3939 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3940 {
3941   DM_Plex *mesh = (DM_Plex *)dm->data;
3942 
3943   PetscFunctionBegin;
3944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3945   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3946   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3947   PetscFunctionReturn(PETSC_SUCCESS);
3948 }
3949 
3950 PetscErrorCode DMSetUp_Plex(DM dm)
3951 {
3952   DM_Plex *mesh = (DM_Plex *)dm->data;
3953   PetscInt size, maxSupportSize;
3954 
3955   PetscFunctionBegin;
3956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3957   PetscCall(PetscSectionSetUp(mesh->coneSection));
3958   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3959   PetscCall(PetscMalloc1(size, &mesh->cones));
3960   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3961   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3962   if (maxSupportSize) {
3963     PetscCall(PetscSectionSetUp(mesh->supportSection));
3964     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3965     PetscCall(PetscMalloc1(size, &mesh->supports));
3966   }
3967   PetscFunctionReturn(PETSC_SUCCESS);
3968 }
3969 
3970 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3971 {
3972   PetscFunctionBegin;
3973   if (subdm) PetscCall(DMClone(dm, subdm));
3974   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3975   if (subdm) (*subdm)->useNatural = dm->useNatural;
3976   if (dm->useNatural && dm->sfMigration) {
3977     PetscSF sfNatural;
3978 
3979     (*subdm)->sfMigration = dm->sfMigration;
3980     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3981     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3982     (*subdm)->sfNatural = sfNatural;
3983   }
3984   PetscFunctionReturn(PETSC_SUCCESS);
3985 }
3986 
3987 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3988 {
3989   PetscInt i = 0;
3990 
3991   PetscFunctionBegin;
3992   PetscCall(DMClone(dms[0], superdm));
3993   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3994   (*superdm)->useNatural = PETSC_FALSE;
3995   for (i = 0; i < len; i++) {
3996     if (dms[i]->useNatural && dms[i]->sfMigration) {
3997       PetscSF sfNatural;
3998 
3999       (*superdm)->sfMigration = dms[i]->sfMigration;
4000       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4001       (*superdm)->useNatural = PETSC_TRUE;
4002       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4003       (*superdm)->sfNatural = sfNatural;
4004       break;
4005     }
4006   }
4007   PetscFunctionReturn(PETSC_SUCCESS);
4008 }
4009 
4010 /*@
4011   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4012 
4013   Not Collective
4014 
4015   Input Parameter:
4016 . dm - The `DMPLEX`
4017 
4018   Level: beginner
4019 
4020   Note:
4021   This should be called after all calls to `DMPlexSetCone()`
4022 
4023 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4024 @*/
4025 PetscErrorCode DMPlexSymmetrize(DM dm)
4026 {
4027   DM_Plex  *mesh = (DM_Plex *)dm->data;
4028   PetscInt *offsets;
4029   PetscInt  supportSize;
4030   PetscInt  pStart, pEnd, p;
4031 
4032   PetscFunctionBegin;
4033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4034   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4035   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4036   /* Calculate support sizes */
4037   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4038   for (p = pStart; p < pEnd; ++p) {
4039     PetscInt dof, off, c;
4040 
4041     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4042     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4043     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4044   }
4045   PetscCall(PetscSectionSetUp(mesh->supportSection));
4046   /* Calculate supports */
4047   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4048   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4049   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4050   for (p = pStart; p < pEnd; ++p) {
4051     PetscInt dof, off, c;
4052 
4053     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4054     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4055     for (c = off; c < off + dof; ++c) {
4056       const PetscInt q = mesh->cones[c];
4057       PetscInt       offS;
4058 
4059       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4060 
4061       mesh->supports[offS + offsets[q]] = p;
4062       ++offsets[q];
4063     }
4064   }
4065   PetscCall(PetscFree(offsets));
4066   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4067   PetscFunctionReturn(PETSC_SUCCESS);
4068 }
4069 
4070 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4071 {
4072   IS stratumIS;
4073 
4074   PetscFunctionBegin;
4075   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4076   if (PetscDefined(USE_DEBUG)) {
4077     PetscInt  qStart, qEnd, numLevels, level;
4078     PetscBool overlap = PETSC_FALSE;
4079     PetscCall(DMLabelGetNumValues(label, &numLevels));
4080     for (level = 0; level < numLevels; level++) {
4081       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4082       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4083         overlap = PETSC_TRUE;
4084         break;
4085       }
4086     }
4087     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);
4088   }
4089   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4090   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4091   PetscCall(ISDestroy(&stratumIS));
4092   PetscFunctionReturn(PETSC_SUCCESS);
4093 }
4094 
4095 /*@
4096   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4097 
4098   Collective
4099 
4100   Input Parameter:
4101 . dm - The `DMPLEX`
4102 
4103   Level: beginner
4104 
4105   Notes:
4106   The strata group all points of the same grade, and this function calculates the strata. This
4107   grade can be seen as the height (or depth) of the point in the DAG.
4108 
4109   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4110   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4111   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4112   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4113   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4114   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4115   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4116 
4117   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4118   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4119   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
4120   to interpolate only that one (e0), so that
4121 .vb
4122   cone(c0) = {e0, v2}
4123   cone(e0) = {v0, v1}
4124 .ve
4125   If `DMPlexStratify()` is run on this mesh, it will give depths
4126 .vb
4127    depth 0 = {v0, v1, v2}
4128    depth 1 = {e0, c0}
4129 .ve
4130   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4131 
4132   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4133 
4134 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4135 @*/
4136 PetscErrorCode DMPlexStratify(DM dm)
4137 {
4138   DM_Plex *mesh = (DM_Plex *)dm->data;
4139   DMLabel  label;
4140   PetscInt pStart, pEnd, p;
4141   PetscInt numRoots = 0, numLeaves = 0;
4142 
4143   PetscFunctionBegin;
4144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4145   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4146 
4147   /* Create depth label */
4148   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4149   PetscCall(DMCreateLabel(dm, "depth"));
4150   PetscCall(DMPlexGetDepthLabel(dm, &label));
4151 
4152   {
4153     /* Initialize roots and count leaves */
4154     PetscInt sMin = PETSC_MAX_INT;
4155     PetscInt sMax = PETSC_MIN_INT;
4156     PetscInt coneSize, supportSize;
4157 
4158     for (p = pStart; p < pEnd; ++p) {
4159       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4160       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4161       if (!coneSize && supportSize) {
4162         sMin = PetscMin(p, sMin);
4163         sMax = PetscMax(p, sMax);
4164         ++numRoots;
4165       } else if (!supportSize && coneSize) {
4166         ++numLeaves;
4167       } else if (!supportSize && !coneSize) {
4168         /* Isolated points */
4169         sMin = PetscMin(p, sMin);
4170         sMax = PetscMax(p, sMax);
4171       }
4172     }
4173     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4174   }
4175 
4176   if (numRoots + numLeaves == (pEnd - pStart)) {
4177     PetscInt sMin = PETSC_MAX_INT;
4178     PetscInt sMax = PETSC_MIN_INT;
4179     PetscInt coneSize, supportSize;
4180 
4181     for (p = pStart; p < pEnd; ++p) {
4182       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4183       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4184       if (!supportSize && coneSize) {
4185         sMin = PetscMin(p, sMin);
4186         sMax = PetscMax(p, sMax);
4187       }
4188     }
4189     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4190   } else {
4191     PetscInt level = 0;
4192     PetscInt qStart, qEnd, q;
4193 
4194     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4195     while (qEnd > qStart) {
4196       PetscInt sMin = PETSC_MAX_INT;
4197       PetscInt sMax = PETSC_MIN_INT;
4198 
4199       for (q = qStart; q < qEnd; ++q) {
4200         const PetscInt *support;
4201         PetscInt        supportSize, s;
4202 
4203         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4204         PetscCall(DMPlexGetSupport(dm, q, &support));
4205         for (s = 0; s < supportSize; ++s) {
4206           sMin = PetscMin(support[s], sMin);
4207           sMax = PetscMax(support[s], sMax);
4208         }
4209       }
4210       PetscCall(DMLabelGetNumValues(label, &level));
4211       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4212       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4213     }
4214   }
4215   { /* just in case there is an empty process */
4216     PetscInt numValues, maxValues = 0, v;
4217 
4218     PetscCall(DMLabelGetNumValues(label, &numValues));
4219     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4220     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4221   }
4222   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4223   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4224   PetscFunctionReturn(PETSC_SUCCESS);
4225 }
4226 
4227 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4228 {
4229   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4230   PetscInt       dim, depth, pheight, coneSize;
4231 
4232   PetscFunctionBeginHot;
4233   PetscCall(DMGetDimension(dm, &dim));
4234   PetscCall(DMPlexGetDepth(dm, &depth));
4235   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4236   pheight = depth - pdepth;
4237   if (depth <= 1) {
4238     switch (pdepth) {
4239     case 0:
4240       ct = DM_POLYTOPE_POINT;
4241       break;
4242     case 1:
4243       switch (coneSize) {
4244       case 2:
4245         ct = DM_POLYTOPE_SEGMENT;
4246         break;
4247       case 3:
4248         ct = DM_POLYTOPE_TRIANGLE;
4249         break;
4250       case 4:
4251         switch (dim) {
4252         case 2:
4253           ct = DM_POLYTOPE_QUADRILATERAL;
4254           break;
4255         case 3:
4256           ct = DM_POLYTOPE_TETRAHEDRON;
4257           break;
4258         default:
4259           break;
4260         }
4261         break;
4262       case 5:
4263         ct = DM_POLYTOPE_PYRAMID;
4264         break;
4265       case 6:
4266         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4267         break;
4268       case 8:
4269         ct = DM_POLYTOPE_HEXAHEDRON;
4270         break;
4271       default:
4272         break;
4273       }
4274     }
4275   } else {
4276     if (pdepth == 0) {
4277       ct = DM_POLYTOPE_POINT;
4278     } else if (pheight == 0) {
4279       switch (dim) {
4280       case 1:
4281         switch (coneSize) {
4282         case 2:
4283           ct = DM_POLYTOPE_SEGMENT;
4284           break;
4285         default:
4286           break;
4287         }
4288         break;
4289       case 2:
4290         switch (coneSize) {
4291         case 3:
4292           ct = DM_POLYTOPE_TRIANGLE;
4293           break;
4294         case 4:
4295           ct = DM_POLYTOPE_QUADRILATERAL;
4296           break;
4297         default:
4298           break;
4299         }
4300         break;
4301       case 3:
4302         switch (coneSize) {
4303         case 4:
4304           ct = DM_POLYTOPE_TETRAHEDRON;
4305           break;
4306         case 5: {
4307           const PetscInt *cone;
4308           PetscInt        faceConeSize;
4309 
4310           PetscCall(DMPlexGetCone(dm, p, &cone));
4311           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4312           switch (faceConeSize) {
4313           case 3:
4314             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4315             break;
4316           case 4:
4317             ct = DM_POLYTOPE_PYRAMID;
4318             break;
4319           }
4320         } break;
4321         case 6:
4322           ct = DM_POLYTOPE_HEXAHEDRON;
4323           break;
4324         default:
4325           break;
4326         }
4327         break;
4328       default:
4329         break;
4330       }
4331     } else if (pheight > 0) {
4332       switch (coneSize) {
4333       case 2:
4334         ct = DM_POLYTOPE_SEGMENT;
4335         break;
4336       case 3:
4337         ct = DM_POLYTOPE_TRIANGLE;
4338         break;
4339       case 4:
4340         ct = DM_POLYTOPE_QUADRILATERAL;
4341         break;
4342       default:
4343         break;
4344       }
4345     }
4346   }
4347   *pt = ct;
4348   PetscFunctionReturn(PETSC_SUCCESS);
4349 }
4350 
4351 /*@
4352   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4353 
4354   Collective
4355 
4356   Input Parameter:
4357 . dm - The `DMPLEX`
4358 
4359   Level: developer
4360 
4361   Note:
4362   This function is normally called automatically when a cell type is requested. It creates an
4363   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4364   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4365 
4366   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4367 
4368 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4369 @*/
4370 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4371 {
4372   DM_Plex *mesh;
4373   DMLabel  ctLabel;
4374   PetscInt pStart, pEnd, p;
4375 
4376   PetscFunctionBegin;
4377   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4378   mesh = (DM_Plex *)dm->data;
4379   PetscCall(DMCreateLabel(dm, "celltype"));
4380   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4381   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4382   PetscCall(PetscFree(mesh->cellTypes));
4383   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4384   for (p = pStart; p < pEnd; ++p) {
4385     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4386     PetscInt       pdepth;
4387 
4388     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4389     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4390     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4391     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4392     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4393   }
4394   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4395   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4396   PetscFunctionReturn(PETSC_SUCCESS);
4397 }
4398 
4399 /*@C
4400   DMPlexGetJoin - Get an array for the join of the set of points
4401 
4402   Not Collective
4403 
4404   Input Parameters:
4405 + dm        - The `DMPLEX` object
4406 . numPoints - The number of input points for the join
4407 - points    - The input points
4408 
4409   Output Parameters:
4410 + numCoveredPoints - The number of points in the join
4411 - coveredPoints    - The points in the join
4412 
4413   Level: intermediate
4414 
4415   Note:
4416   Currently, this is restricted to a single level join
4417 
4418   Fortran Notes:
4419   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4420 
4421 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4422 @*/
4423 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4424 {
4425   DM_Plex  *mesh = (DM_Plex *)dm->data;
4426   PetscInt *join[2];
4427   PetscInt  joinSize, i = 0;
4428   PetscInt  dof, off, p, c, m;
4429   PetscInt  maxSupportSize;
4430 
4431   PetscFunctionBegin;
4432   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4433   PetscAssertPointer(points, 3);
4434   PetscAssertPointer(numCoveredPoints, 4);
4435   PetscAssertPointer(coveredPoints, 5);
4436   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4437   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4438   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4439   /* Copy in support of first point */
4440   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4441   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4442   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4443   /* Check each successive support */
4444   for (p = 1; p < numPoints; ++p) {
4445     PetscInt newJoinSize = 0;
4446 
4447     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4448     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4449     for (c = 0; c < dof; ++c) {
4450       const PetscInt point = mesh->supports[off + c];
4451 
4452       for (m = 0; m < joinSize; ++m) {
4453         if (point == join[i][m]) {
4454           join[1 - i][newJoinSize++] = point;
4455           break;
4456         }
4457       }
4458     }
4459     joinSize = newJoinSize;
4460     i        = 1 - i;
4461   }
4462   *numCoveredPoints = joinSize;
4463   *coveredPoints    = join[i];
4464   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4465   PetscFunctionReturn(PETSC_SUCCESS);
4466 }
4467 
4468 /*@C
4469   DMPlexRestoreJoin - Restore an array for the join of the set of points
4470 
4471   Not Collective
4472 
4473   Input Parameters:
4474 + dm        - The `DMPLEX` object
4475 . numPoints - The number of input points for the join
4476 - points    - The input points
4477 
4478   Output Parameters:
4479 + numCoveredPoints - The number of points in the join
4480 - coveredPoints    - The points in the join
4481 
4482   Level: intermediate
4483 
4484   Fortran Notes:
4485   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4486 
4487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4488 @*/
4489 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4490 {
4491   PetscFunctionBegin;
4492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4493   if (points) PetscAssertPointer(points, 3);
4494   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4495   PetscAssertPointer(coveredPoints, 5);
4496   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4497   if (numCoveredPoints) *numCoveredPoints = 0;
4498   PetscFunctionReturn(PETSC_SUCCESS);
4499 }
4500 
4501 /*@C
4502   DMPlexGetFullJoin - Get an array for the join of the set of points
4503 
4504   Not Collective
4505 
4506   Input Parameters:
4507 + dm        - The `DMPLEX` object
4508 . numPoints - The number of input points for the join
4509 - points    - The input points
4510 
4511   Output Parameters:
4512 + numCoveredPoints - The number of points in the join
4513 - coveredPoints    - The points in the join
4514 
4515   Level: intermediate
4516 
4517   Fortran Notes:
4518   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4519 
4520 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4521 @*/
4522 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4523 {
4524   PetscInt *offsets, **closures;
4525   PetscInt *join[2];
4526   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4527   PetscInt  p, d, c, m, ms;
4528 
4529   PetscFunctionBegin;
4530   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4531   PetscAssertPointer(points, 3);
4532   PetscAssertPointer(numCoveredPoints, 4);
4533   PetscAssertPointer(coveredPoints, 5);
4534 
4535   PetscCall(DMPlexGetDepth(dm, &depth));
4536   PetscCall(PetscCalloc1(numPoints, &closures));
4537   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4538   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4539   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4540   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4541   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4542 
4543   for (p = 0; p < numPoints; ++p) {
4544     PetscInt closureSize;
4545 
4546     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4547 
4548     offsets[p * (depth + 2) + 0] = 0;
4549     for (d = 0; d < depth + 1; ++d) {
4550       PetscInt pStart, pEnd, i;
4551 
4552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4553       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4554         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4555           offsets[p * (depth + 2) + d + 1] = i;
4556           break;
4557         }
4558       }
4559       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4560     }
4561     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);
4562   }
4563   for (d = 0; d < depth + 1; ++d) {
4564     PetscInt dof;
4565 
4566     /* Copy in support of first point */
4567     dof = offsets[d + 1] - offsets[d];
4568     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4569     /* Check each successive cone */
4570     for (p = 1; p < numPoints && joinSize; ++p) {
4571       PetscInt newJoinSize = 0;
4572 
4573       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4574       for (c = 0; c < dof; ++c) {
4575         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4576 
4577         for (m = 0; m < joinSize; ++m) {
4578           if (point == join[i][m]) {
4579             join[1 - i][newJoinSize++] = point;
4580             break;
4581           }
4582         }
4583       }
4584       joinSize = newJoinSize;
4585       i        = 1 - i;
4586     }
4587     if (joinSize) break;
4588   }
4589   *numCoveredPoints = joinSize;
4590   *coveredPoints    = join[i];
4591   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4592   PetscCall(PetscFree(closures));
4593   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4594   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4595   PetscFunctionReturn(PETSC_SUCCESS);
4596 }
4597 
4598 /*@C
4599   DMPlexGetMeet - Get an array for the meet of the set of points
4600 
4601   Not Collective
4602 
4603   Input Parameters:
4604 + dm        - The `DMPLEX` object
4605 . numPoints - The number of input points for the meet
4606 - points    - The input points
4607 
4608   Output Parameters:
4609 + numCoveringPoints - The number of points in the meet
4610 - coveringPoints    - The points in the meet
4611 
4612   Level: intermediate
4613 
4614   Note:
4615   Currently, this is restricted to a single level meet
4616 
4617   Fortran Notes:
4618   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4619 
4620 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4621 @*/
4622 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4623 {
4624   DM_Plex  *mesh = (DM_Plex *)dm->data;
4625   PetscInt *meet[2];
4626   PetscInt  meetSize, i = 0;
4627   PetscInt  dof, off, p, c, m;
4628   PetscInt  maxConeSize;
4629 
4630   PetscFunctionBegin;
4631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4632   PetscAssertPointer(points, 3);
4633   PetscAssertPointer(numCoveringPoints, 4);
4634   PetscAssertPointer(coveringPoints, 5);
4635   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4636   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4637   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4638   /* Copy in cone of first point */
4639   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4640   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4641   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4642   /* Check each successive cone */
4643   for (p = 1; p < numPoints; ++p) {
4644     PetscInt newMeetSize = 0;
4645 
4646     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4647     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4648     for (c = 0; c < dof; ++c) {
4649       const PetscInt point = mesh->cones[off + c];
4650 
4651       for (m = 0; m < meetSize; ++m) {
4652         if (point == meet[i][m]) {
4653           meet[1 - i][newMeetSize++] = point;
4654           break;
4655         }
4656       }
4657     }
4658     meetSize = newMeetSize;
4659     i        = 1 - i;
4660   }
4661   *numCoveringPoints = meetSize;
4662   *coveringPoints    = meet[i];
4663   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4664   PetscFunctionReturn(PETSC_SUCCESS);
4665 }
4666 
4667 /*@C
4668   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4669 
4670   Not Collective
4671 
4672   Input Parameters:
4673 + dm        - The `DMPLEX` object
4674 . numPoints - The number of input points for the meet
4675 - points    - The input points
4676 
4677   Output Parameters:
4678 + numCoveredPoints - The number of points in the meet
4679 - coveredPoints    - The points in the meet
4680 
4681   Level: intermediate
4682 
4683   Fortran Notes:
4684   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4685 
4686 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4687 @*/
4688 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4689 {
4690   PetscFunctionBegin;
4691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4692   if (points) PetscAssertPointer(points, 3);
4693   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4694   PetscAssertPointer(coveredPoints, 5);
4695   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4696   if (numCoveredPoints) *numCoveredPoints = 0;
4697   PetscFunctionReturn(PETSC_SUCCESS);
4698 }
4699 
4700 /*@C
4701   DMPlexGetFullMeet - Get an array for the meet of the set of points
4702 
4703   Not Collective
4704 
4705   Input Parameters:
4706 + dm        - The `DMPLEX` object
4707 . numPoints - The number of input points for the meet
4708 - points    - The input points
4709 
4710   Output Parameters:
4711 + numCoveredPoints - The number of points in the meet
4712 - coveredPoints    - The points in the meet
4713 
4714   Level: intermediate
4715 
4716   Fortran Notes:
4717   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4718 
4719 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4720 @*/
4721 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4722 {
4723   PetscInt *offsets, **closures;
4724   PetscInt *meet[2];
4725   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4726   PetscInt  p, h, c, m, mc;
4727 
4728   PetscFunctionBegin;
4729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4730   PetscAssertPointer(points, 3);
4731   PetscAssertPointer(numCoveredPoints, 4);
4732   PetscAssertPointer(coveredPoints, 5);
4733 
4734   PetscCall(DMPlexGetDepth(dm, &height));
4735   PetscCall(PetscMalloc1(numPoints, &closures));
4736   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4737   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4738   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4739   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4740   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4741 
4742   for (p = 0; p < numPoints; ++p) {
4743     PetscInt closureSize;
4744 
4745     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4746 
4747     offsets[p * (height + 2) + 0] = 0;
4748     for (h = 0; h < height + 1; ++h) {
4749       PetscInt pStart, pEnd, i;
4750 
4751       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4752       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4753         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4754           offsets[p * (height + 2) + h + 1] = i;
4755           break;
4756         }
4757       }
4758       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4759     }
4760     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);
4761   }
4762   for (h = 0; h < height + 1; ++h) {
4763     PetscInt dof;
4764 
4765     /* Copy in cone of first point */
4766     dof = offsets[h + 1] - offsets[h];
4767     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4768     /* Check each successive cone */
4769     for (p = 1; p < numPoints && meetSize; ++p) {
4770       PetscInt newMeetSize = 0;
4771 
4772       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4773       for (c = 0; c < dof; ++c) {
4774         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4775 
4776         for (m = 0; m < meetSize; ++m) {
4777           if (point == meet[i][m]) {
4778             meet[1 - i][newMeetSize++] = point;
4779             break;
4780           }
4781         }
4782       }
4783       meetSize = newMeetSize;
4784       i        = 1 - i;
4785     }
4786     if (meetSize) break;
4787   }
4788   *numCoveredPoints = meetSize;
4789   *coveredPoints    = meet[i];
4790   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4791   PetscCall(PetscFree(closures));
4792   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4793   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4794   PetscFunctionReturn(PETSC_SUCCESS);
4795 }
4796 
4797 /*@C
4798   DMPlexEqual - Determine if two `DM` have the same topology
4799 
4800   Not Collective
4801 
4802   Input Parameters:
4803 + dmA - A `DMPLEX` object
4804 - dmB - A `DMPLEX` object
4805 
4806   Output Parameter:
4807 . equal - `PETSC_TRUE` if the topologies are identical
4808 
4809   Level: intermediate
4810 
4811   Note:
4812   We are not solving graph isomorphism, so we do not permute.
4813 
4814 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4815 @*/
4816 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4817 {
4818   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4819 
4820   PetscFunctionBegin;
4821   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4822   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4823   PetscAssertPointer(equal, 3);
4824 
4825   *equal = PETSC_FALSE;
4826   PetscCall(DMPlexGetDepth(dmA, &depth));
4827   PetscCall(DMPlexGetDepth(dmB, &depthB));
4828   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4829   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4830   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4831   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4832   for (p = pStart; p < pEnd; ++p) {
4833     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4834     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4835 
4836     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4837     PetscCall(DMPlexGetCone(dmA, p, &cone));
4838     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4839     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4840     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4841     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4842     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4843     for (c = 0; c < coneSize; ++c) {
4844       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4845       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4846     }
4847     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4848     PetscCall(DMPlexGetSupport(dmA, p, &support));
4849     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4850     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4851     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4852     for (s = 0; s < supportSize; ++s) {
4853       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4854     }
4855   }
4856   *equal = PETSC_TRUE;
4857   PetscFunctionReturn(PETSC_SUCCESS);
4858 }
4859 
4860 /*@C
4861   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4862 
4863   Not Collective
4864 
4865   Input Parameters:
4866 + dm         - The `DMPLEX`
4867 . cellDim    - The cell dimension
4868 - numCorners - The number of vertices on a cell
4869 
4870   Output Parameter:
4871 . numFaceVertices - The number of vertices on a face
4872 
4873   Level: developer
4874 
4875   Note:
4876   Of course this can only work for a restricted set of symmetric shapes
4877 
4878 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4879 @*/
4880 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4881 {
4882   MPI_Comm comm;
4883 
4884   PetscFunctionBegin;
4885   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4886   PetscAssertPointer(numFaceVertices, 4);
4887   switch (cellDim) {
4888   case 0:
4889     *numFaceVertices = 0;
4890     break;
4891   case 1:
4892     *numFaceVertices = 1;
4893     break;
4894   case 2:
4895     switch (numCorners) {
4896     case 3:                 /* triangle */
4897       *numFaceVertices = 2; /* Edge has 2 vertices */
4898       break;
4899     case 4:                 /* quadrilateral */
4900       *numFaceVertices = 2; /* Edge has 2 vertices */
4901       break;
4902     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4903       *numFaceVertices = 3; /* Edge has 3 vertices */
4904       break;
4905     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4906       *numFaceVertices = 3; /* Edge has 3 vertices */
4907       break;
4908     default:
4909       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4910     }
4911     break;
4912   case 3:
4913     switch (numCorners) {
4914     case 4:                 /* tetradehdron */
4915       *numFaceVertices = 3; /* Face has 3 vertices */
4916       break;
4917     case 6:                 /* tet cohesive cells */
4918       *numFaceVertices = 4; /* Face has 4 vertices */
4919       break;
4920     case 8:                 /* hexahedron */
4921       *numFaceVertices = 4; /* Face has 4 vertices */
4922       break;
4923     case 9:                 /* tet cohesive Lagrange cells */
4924       *numFaceVertices = 6; /* Face has 6 vertices */
4925       break;
4926     case 10:                /* quadratic tetrahedron */
4927       *numFaceVertices = 6; /* Face has 6 vertices */
4928       break;
4929     case 12:                /* hex cohesive Lagrange cells */
4930       *numFaceVertices = 6; /* Face has 6 vertices */
4931       break;
4932     case 18:                /* quadratic tet cohesive Lagrange cells */
4933       *numFaceVertices = 6; /* Face has 6 vertices */
4934       break;
4935     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4936       *numFaceVertices = 9; /* Face has 9 vertices */
4937       break;
4938     default:
4939       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4940     }
4941     break;
4942   default:
4943     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4944   }
4945   PetscFunctionReturn(PETSC_SUCCESS);
4946 }
4947 
4948 /*@
4949   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4950 
4951   Not Collective
4952 
4953   Input Parameter:
4954 . dm - The `DMPLEX` object
4955 
4956   Output Parameter:
4957 . depthLabel - The `DMLabel` recording point depth
4958 
4959   Level: developer
4960 
4961 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4962 @*/
4963 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4964 {
4965   PetscFunctionBegin;
4966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4967   PetscAssertPointer(depthLabel, 2);
4968   *depthLabel = dm->depthLabel;
4969   PetscFunctionReturn(PETSC_SUCCESS);
4970 }
4971 
4972 /*@
4973   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4974 
4975   Not Collective
4976 
4977   Input Parameter:
4978 . dm - The `DMPLEX` object
4979 
4980   Output Parameter:
4981 . depth - The number of strata (breadth first levels) in the DAG
4982 
4983   Level: developer
4984 
4985   Notes:
4986   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4987 
4988   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4989 
4990   An empty mesh gives -1.
4991 
4992 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4993 @*/
4994 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4995 {
4996   DM_Plex *mesh = (DM_Plex *)dm->data;
4997   DMLabel  label;
4998   PetscInt d = 0;
4999 
5000   PetscFunctionBegin;
5001   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5002   PetscAssertPointer(depth, 2);
5003   if (mesh->tr) {
5004     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5005   } else {
5006     PetscCall(DMPlexGetDepthLabel(dm, &label));
5007     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5008     *depth = d - 1;
5009   }
5010   PetscFunctionReturn(PETSC_SUCCESS);
5011 }
5012 
5013 /*@
5014   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5015 
5016   Not Collective
5017 
5018   Input Parameters:
5019 + dm    - The `DMPLEX` object
5020 - depth - The requested depth
5021 
5022   Output Parameters:
5023 + start - The first point at this `depth`
5024 - end   - One beyond the last point at this `depth`
5025 
5026   Level: developer
5027 
5028   Notes:
5029   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5030   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5031   higher dimension, e.g., "edges".
5032 
5033 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5034 @*/
5035 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5036 {
5037   DM_Plex *mesh = (DM_Plex *)dm->data;
5038   DMLabel  label;
5039   PetscInt pStart, pEnd;
5040 
5041   PetscFunctionBegin;
5042   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5043   if (start) {
5044     PetscAssertPointer(start, 3);
5045     *start = 0;
5046   }
5047   if (end) {
5048     PetscAssertPointer(end, 4);
5049     *end = 0;
5050   }
5051   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5052   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5053   if (depth < 0) {
5054     if (start) *start = pStart;
5055     if (end) *end = pEnd;
5056     PetscFunctionReturn(PETSC_SUCCESS);
5057   }
5058   if (mesh->tr) {
5059     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5060   } else {
5061     PetscCall(DMPlexGetDepthLabel(dm, &label));
5062     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5063     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5064   }
5065   PetscFunctionReturn(PETSC_SUCCESS);
5066 }
5067 
5068 /*@
5069   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5070 
5071   Not Collective
5072 
5073   Input Parameters:
5074 + dm     - The `DMPLEX` object
5075 - height - The requested height
5076 
5077   Output Parameters:
5078 + start - The first point at this `height`
5079 - end   - One beyond the last point at this `height`
5080 
5081   Level: developer
5082 
5083   Notes:
5084   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5085   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5086   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5087 
5088 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5089 @*/
5090 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5091 {
5092   DMLabel  label;
5093   PetscInt depth, pStart, pEnd;
5094 
5095   PetscFunctionBegin;
5096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5097   if (start) {
5098     PetscAssertPointer(start, 3);
5099     *start = 0;
5100   }
5101   if (end) {
5102     PetscAssertPointer(end, 4);
5103     *end = 0;
5104   }
5105   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5106   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5107   if (height < 0) {
5108     if (start) *start = pStart;
5109     if (end) *end = pEnd;
5110     PetscFunctionReturn(PETSC_SUCCESS);
5111   }
5112   PetscCall(DMPlexGetDepthLabel(dm, &label));
5113   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5114   else PetscCall(DMGetDimension(dm, &depth));
5115   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5116   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5117   PetscFunctionReturn(PETSC_SUCCESS);
5118 }
5119 
5120 /*@
5121   DMPlexGetPointDepth - Get the `depth` of a given point
5122 
5123   Not Collective
5124 
5125   Input Parameters:
5126 + dm    - The `DMPLEX` object
5127 - point - The point
5128 
5129   Output Parameter:
5130 . depth - The depth of the `point`
5131 
5132   Level: intermediate
5133 
5134 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5135 @*/
5136 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5137 {
5138   PetscFunctionBegin;
5139   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5140   PetscAssertPointer(depth, 3);
5141   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5142   PetscFunctionReturn(PETSC_SUCCESS);
5143 }
5144 
5145 /*@
5146   DMPlexGetPointHeight - Get the `height` of a given point
5147 
5148   Not Collective
5149 
5150   Input Parameters:
5151 + dm    - The `DMPLEX` object
5152 - point - The point
5153 
5154   Output Parameter:
5155 . height - The height of the `point`
5156 
5157   Level: intermediate
5158 
5159 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5160 @*/
5161 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5162 {
5163   PetscInt n, pDepth;
5164 
5165   PetscFunctionBegin;
5166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5167   PetscAssertPointer(height, 3);
5168   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5169   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5170   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5171   PetscFunctionReturn(PETSC_SUCCESS);
5172 }
5173 
5174 /*@
5175   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5176 
5177   Not Collective
5178 
5179   Input Parameter:
5180 . dm - The `DMPLEX` object
5181 
5182   Output Parameter:
5183 . celltypeLabel - The `DMLabel` recording cell polytope type
5184 
5185   Level: developer
5186 
5187   Note:
5188   This function will trigger automatica computation of cell types. This can be disabled by calling
5189   `DMCreateLabel`(dm, "celltype") beforehand.
5190 
5191 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5192 @*/
5193 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5194 {
5195   PetscFunctionBegin;
5196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5197   PetscAssertPointer(celltypeLabel, 2);
5198   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5199   *celltypeLabel = dm->celltypeLabel;
5200   PetscFunctionReturn(PETSC_SUCCESS);
5201 }
5202 
5203 /*@
5204   DMPlexGetCellType - Get the polytope type of a given cell
5205 
5206   Not Collective
5207 
5208   Input Parameters:
5209 + dm   - The `DMPLEX` object
5210 - cell - The cell
5211 
5212   Output Parameter:
5213 . celltype - The polytope type of the cell
5214 
5215   Level: intermediate
5216 
5217 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5218 @*/
5219 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5220 {
5221   DM_Plex *mesh = (DM_Plex *)dm->data;
5222   DMLabel  label;
5223   PetscInt ct;
5224 
5225   PetscFunctionBegin;
5226   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5227   PetscAssertPointer(celltype, 3);
5228   if (mesh->tr) {
5229     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5230   } else {
5231     PetscInt pStart, pEnd;
5232 
5233     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5234     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5235       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5236       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5237       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5238       for (PetscInt p = pStart; p < pEnd; p++) {
5239         PetscCall(DMLabelGetValue(label, p, &ct));
5240         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5241       }
5242     }
5243     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5244     if (PetscDefined(USE_DEBUG)) {
5245       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5246       PetscCall(DMLabelGetValue(label, cell, &ct));
5247       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5248       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5249     }
5250   }
5251   PetscFunctionReturn(PETSC_SUCCESS);
5252 }
5253 
5254 /*@
5255   DMPlexSetCellType - Set the polytope type of a given cell
5256 
5257   Not Collective
5258 
5259   Input Parameters:
5260 + dm       - The `DMPLEX` object
5261 . cell     - The cell
5262 - celltype - The polytope type of the cell
5263 
5264   Level: advanced
5265 
5266   Note:
5267   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5268   is executed. This function will override the computed type. However, if automatic classification will not succeed
5269   and a user wants to manually specify all types, the classification must be disabled by calling
5270   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5271 
5272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5273 @*/
5274 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5275 {
5276   DM_Plex *mesh = (DM_Plex *)dm->data;
5277   DMLabel  label;
5278   PetscInt pStart, pEnd;
5279 
5280   PetscFunctionBegin;
5281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5282   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5283   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5284   PetscCall(DMLabelSetValue(label, cell, celltype));
5285   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5286   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5287   PetscFunctionReturn(PETSC_SUCCESS);
5288 }
5289 
5290 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5291 {
5292   PetscSection section, s;
5293   Mat          m;
5294   PetscInt     maxHeight;
5295   const char  *prefix;
5296 
5297   PetscFunctionBegin;
5298   PetscCall(DMClone(dm, cdm));
5299   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5300   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5301   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5302   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5303   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5304   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5305   PetscCall(DMSetLocalSection(*cdm, section));
5306   PetscCall(PetscSectionDestroy(&section));
5307   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5308   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5309   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5310   PetscCall(PetscSectionDestroy(&s));
5311   PetscCall(MatDestroy(&m));
5312 
5313   PetscCall(DMSetNumFields(*cdm, 1));
5314   PetscCall(DMCreateDS(*cdm));
5315   (*cdm)->cloneOpts = PETSC_TRUE;
5316   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5317   PetscFunctionReturn(PETSC_SUCCESS);
5318 }
5319 
5320 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5321 {
5322   Vec coordsLocal, cellCoordsLocal;
5323   DM  coordsDM, cellCoordsDM;
5324 
5325   PetscFunctionBegin;
5326   *field = NULL;
5327   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5328   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5329   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5330   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5331   if (coordsLocal && coordsDM) {
5332     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5333     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5334   }
5335   PetscFunctionReturn(PETSC_SUCCESS);
5336 }
5337 
5338 /*@C
5339   DMPlexGetConeSection - Return a section which describes the layout of cone data
5340 
5341   Not Collective
5342 
5343   Input Parameter:
5344 . dm - The `DMPLEX` object
5345 
5346   Output Parameter:
5347 . section - The `PetscSection` object
5348 
5349   Level: developer
5350 
5351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5352 @*/
5353 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5354 {
5355   DM_Plex *mesh = (DM_Plex *)dm->data;
5356 
5357   PetscFunctionBegin;
5358   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5359   if (section) *section = mesh->coneSection;
5360   PetscFunctionReturn(PETSC_SUCCESS);
5361 }
5362 
5363 /*@C
5364   DMPlexGetSupportSection - Return a section which describes the layout of support data
5365 
5366   Not Collective
5367 
5368   Input Parameter:
5369 . dm - The `DMPLEX` object
5370 
5371   Output Parameter:
5372 . section - The `PetscSection` object
5373 
5374   Level: developer
5375 
5376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5377 @*/
5378 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5379 {
5380   DM_Plex *mesh = (DM_Plex *)dm->data;
5381 
5382   PetscFunctionBegin;
5383   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5384   if (section) *section = mesh->supportSection;
5385   PetscFunctionReturn(PETSC_SUCCESS);
5386 }
5387 
5388 /*@C
5389   DMPlexGetCones - Return cone data
5390 
5391   Not Collective
5392 
5393   Input Parameter:
5394 . dm - The `DMPLEX` object
5395 
5396   Output Parameter:
5397 . cones - The cone for each point
5398 
5399   Level: developer
5400 
5401 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5402 @*/
5403 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5404 {
5405   DM_Plex *mesh = (DM_Plex *)dm->data;
5406 
5407   PetscFunctionBegin;
5408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5409   if (cones) *cones = mesh->cones;
5410   PetscFunctionReturn(PETSC_SUCCESS);
5411 }
5412 
5413 /*@C
5414   DMPlexGetConeOrientations - Return cone orientation data
5415 
5416   Not Collective
5417 
5418   Input Parameter:
5419 . dm - The `DMPLEX` object
5420 
5421   Output Parameter:
5422 . coneOrientations - The array of cone orientations for all points
5423 
5424   Level: developer
5425 
5426   Notes:
5427   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5428 
5429   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5430 
5431 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5432 @*/
5433 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5434 {
5435   DM_Plex *mesh = (DM_Plex *)dm->data;
5436 
5437   PetscFunctionBegin;
5438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5439   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5440   PetscFunctionReturn(PETSC_SUCCESS);
5441 }
5442 
5443 /******************************** FEM Support **********************************/
5444 
5445 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5446 {
5447   PetscInt depth;
5448 
5449   PetscFunctionBegin;
5450   PetscCall(DMPlexGetDepth(plex, &depth));
5451   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5452   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5453   PetscFunctionReturn(PETSC_SUCCESS);
5454 }
5455 
5456 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5457 {
5458   PetscInt depth;
5459 
5460   PetscFunctionBegin;
5461   PetscCall(DMPlexGetDepth(plex, &depth));
5462   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5463   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5464   PetscFunctionReturn(PETSC_SUCCESS);
5465 }
5466 
5467 /*
5468  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5469  representing a line in the section.
5470 */
5471 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5472 {
5473   PetscFunctionBeginHot;
5474   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5475   if (line < 0) {
5476     *k  = 0;
5477     *Nc = 0;
5478   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5479     *k = 1;
5480   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5481     /* An order k SEM disc has k-1 dofs on an edge */
5482     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5483     *k = *k / *Nc + 1;
5484   }
5485   PetscFunctionReturn(PETSC_SUCCESS);
5486 }
5487 
5488 /*@
5489 
5490   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5491   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5492   section provided (or the section of the `DM`).
5493 
5494   Input Parameters:
5495 + dm      - The `DM`
5496 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5497 - section - The `PetscSection` to reorder, or `NULL` for the default section
5498 
5499   Example:
5500   A typical interpolated single-quad mesh might order points as
5501 .vb
5502   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5503 
5504   v4 -- e6 -- v3
5505   |           |
5506   e7    c0    e8
5507   |           |
5508   v1 -- e5 -- v2
5509 .ve
5510 
5511   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5512   dofs in the order of points, e.g.,
5513 .vb
5514     c0 -> [0,1,2,3]
5515     v1 -> [4]
5516     ...
5517     e5 -> [8, 9]
5518 .ve
5519 
5520   which corresponds to the dofs
5521 .vb
5522     6   10  11  7
5523     13  2   3   15
5524     12  0   1   14
5525     4   8   9   5
5526 .ve
5527 
5528   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5529 .vb
5530   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5531 .ve
5532 
5533   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5534 .vb
5535    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5536 .ve
5537 
5538   Level: developer
5539 
5540   Notes:
5541   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5542   degree of the basis.
5543 
5544   This is required to run with libCEED.
5545 
5546 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5547 @*/
5548 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5549 {
5550   DMLabel   label;
5551   PetscInt  dim, depth = -1, eStart = -1, Nf;
5552   PetscBool vertexchart;
5553 
5554   PetscFunctionBegin;
5555   PetscCall(DMGetDimension(dm, &dim));
5556   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5557   if (point < 0) {
5558     PetscInt sStart, sEnd;
5559 
5560     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5561     point = sEnd - sStart ? sStart : point;
5562   }
5563   PetscCall(DMPlexGetDepthLabel(dm, &label));
5564   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5565   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5566   if (depth == 1) {
5567     eStart = point;
5568   } else if (depth == dim) {
5569     const PetscInt *cone;
5570 
5571     PetscCall(DMPlexGetCone(dm, point, &cone));
5572     if (dim == 2) eStart = cone[0];
5573     else if (dim == 3) {
5574       const PetscInt *cone2;
5575       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5576       eStart = cone2[0];
5577     } 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);
5578   } 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);
5579   { /* Determine whether the chart covers all points or just vertices. */
5580     PetscInt pStart, pEnd, cStart, cEnd;
5581     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5582     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5583     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5584     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5585     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5586   }
5587   PetscCall(PetscSectionGetNumFields(section, &Nf));
5588   for (PetscInt d = 1; d <= dim; d++) {
5589     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5590     PetscInt *perm;
5591 
5592     for (f = 0; f < Nf; ++f) {
5593       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5594       size += PetscPowInt(k + 1, d) * Nc;
5595     }
5596     PetscCall(PetscMalloc1(size, &perm));
5597     for (f = 0; f < Nf; ++f) {
5598       switch (d) {
5599       case 1:
5600         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5601         /*
5602          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5603          We want              [ vtx0; edge of length k-1; vtx1 ]
5604          */
5605         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5606         for (i = 0; i < k - 1; i++)
5607           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5608         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5609         foffset = offset;
5610         break;
5611       case 2:
5612         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5613         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5614         /* The SEM order is
5615 
5616          v_lb, {e_b}, v_rb,
5617          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5618          v_lt, reverse {e_t}, v_rt
5619          */
5620         {
5621           const PetscInt of   = 0;
5622           const PetscInt oeb  = of + PetscSqr(k - 1);
5623           const PetscInt oer  = oeb + (k - 1);
5624           const PetscInt oet  = oer + (k - 1);
5625           const PetscInt oel  = oet + (k - 1);
5626           const PetscInt ovlb = oel + (k - 1);
5627           const PetscInt ovrb = ovlb + 1;
5628           const PetscInt ovrt = ovrb + 1;
5629           const PetscInt ovlt = ovrt + 1;
5630           PetscInt       o;
5631 
5632           /* bottom */
5633           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5634           for (o = oeb; o < oer; ++o)
5635             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5636           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5637           /* middle */
5638           for (i = 0; i < k - 1; ++i) {
5639             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5640             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5641               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5642             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5643           }
5644           /* top */
5645           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5646           for (o = oel - 1; o >= oet; --o)
5647             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5648           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5649           foffset = offset;
5650         }
5651         break;
5652       case 3:
5653         /* The original hex closure is
5654 
5655          {c,
5656          f_b, f_t, f_f, f_b, f_r, f_l,
5657          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5658          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5659          */
5660         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5661         /* The SEM order is
5662          Bottom Slice
5663          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5664          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5665          v_blb, {e_bb}, v_brb,
5666 
5667          Middle Slice (j)
5668          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5669          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5670          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5671 
5672          Top Slice
5673          v_tlf, {e_tf}, v_trf,
5674          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5675          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5676          */
5677         {
5678           const PetscInt oc    = 0;
5679           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5680           const PetscInt oft   = ofb + PetscSqr(k - 1);
5681           const PetscInt off   = oft + PetscSqr(k - 1);
5682           const PetscInt ofk   = off + PetscSqr(k - 1);
5683           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5684           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5685           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5686           const PetscInt oebb  = oebl + (k - 1);
5687           const PetscInt oebr  = oebb + (k - 1);
5688           const PetscInt oebf  = oebr + (k - 1);
5689           const PetscInt oetf  = oebf + (k - 1);
5690           const PetscInt oetr  = oetf + (k - 1);
5691           const PetscInt oetb  = oetr + (k - 1);
5692           const PetscInt oetl  = oetb + (k - 1);
5693           const PetscInt oerf  = oetl + (k - 1);
5694           const PetscInt oelf  = oerf + (k - 1);
5695           const PetscInt oelb  = oelf + (k - 1);
5696           const PetscInt oerb  = oelb + (k - 1);
5697           const PetscInt ovblf = oerb + (k - 1);
5698           const PetscInt ovblb = ovblf + 1;
5699           const PetscInt ovbrb = ovblb + 1;
5700           const PetscInt ovbrf = ovbrb + 1;
5701           const PetscInt ovtlf = ovbrf + 1;
5702           const PetscInt ovtrf = ovtlf + 1;
5703           const PetscInt ovtrb = ovtrf + 1;
5704           const PetscInt ovtlb = ovtrb + 1;
5705           PetscInt       o, n;
5706 
5707           /* Bottom Slice */
5708           /*   bottom */
5709           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5710           for (o = oetf - 1; o >= oebf; --o)
5711             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5712           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5713           /*   middle */
5714           for (i = 0; i < k - 1; ++i) {
5715             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5716             for (n = 0; n < k - 1; ++n) {
5717               o = ofb + n * (k - 1) + i;
5718               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5719             }
5720             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5721           }
5722           /*   top */
5723           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5724           for (o = oebb; o < oebr; ++o)
5725             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5726           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5727 
5728           /* Middle Slice */
5729           for (j = 0; j < k - 1; ++j) {
5730             /*   bottom */
5731             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5732             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5733               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5734             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5735             /*   middle */
5736             for (i = 0; i < k - 1; ++i) {
5737               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5738               for (n = 0; n < k - 1; ++n)
5739                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5740               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5741             }
5742             /*   top */
5743             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5744             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5745               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5746             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5747           }
5748 
5749           /* Top Slice */
5750           /*   bottom */
5751           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5752           for (o = oetf; o < oetr; ++o)
5753             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5754           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5755           /*   middle */
5756           for (i = 0; i < k - 1; ++i) {
5757             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5758             for (n = 0; n < k - 1; ++n)
5759               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5760             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5761           }
5762           /*   top */
5763           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5764           for (o = oetl - 1; o >= oetb; --o)
5765             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5766           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5767 
5768           foffset = offset;
5769         }
5770         break;
5771       default:
5772         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5773       }
5774     }
5775     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5776     /* Check permutation */
5777     {
5778       PetscInt *check;
5779 
5780       PetscCall(PetscMalloc1(size, &check));
5781       for (i = 0; i < size; ++i) {
5782         check[i] = -1;
5783         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5784       }
5785       for (i = 0; i < size; ++i) check[perm[i]] = i;
5786       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5787       PetscCall(PetscFree(check));
5788     }
5789     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5790     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5791       PetscInt *loc_perm;
5792       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5793       for (PetscInt i = 0; i < size; i++) {
5794         loc_perm[i]        = perm[i];
5795         loc_perm[size + i] = size + perm[i];
5796       }
5797       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5798     }
5799   }
5800   PetscFunctionReturn(PETSC_SUCCESS);
5801 }
5802 
5803 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5804 {
5805   PetscDS  prob;
5806   PetscInt depth, Nf, h;
5807   DMLabel  label;
5808 
5809   PetscFunctionBeginHot;
5810   PetscCall(DMGetDS(dm, &prob));
5811   Nf      = prob->Nf;
5812   label   = dm->depthLabel;
5813   *dspace = NULL;
5814   if (field < Nf) {
5815     PetscObject disc = prob->disc[field];
5816 
5817     if (disc->classid == PETSCFE_CLASSID) {
5818       PetscDualSpace dsp;
5819 
5820       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5821       PetscCall(DMLabelGetNumValues(label, &depth));
5822       PetscCall(DMLabelGetValue(label, point, &h));
5823       h = depth - 1 - h;
5824       if (h) {
5825         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5826       } else {
5827         *dspace = dsp;
5828       }
5829     }
5830   }
5831   PetscFunctionReturn(PETSC_SUCCESS);
5832 }
5833 
5834 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5835 {
5836   PetscScalar       *array;
5837   const PetscScalar *vArray;
5838   const PetscInt    *cone, *coneO;
5839   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5840 
5841   PetscFunctionBeginHot;
5842   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5843   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5844   PetscCall(DMPlexGetCone(dm, point, &cone));
5845   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5846   if (!values || !*values) {
5847     if ((point >= pStart) && (point < pEnd)) {
5848       PetscInt dof;
5849 
5850       PetscCall(PetscSectionGetDof(section, point, &dof));
5851       size += dof;
5852     }
5853     for (p = 0; p < numPoints; ++p) {
5854       const PetscInt cp = cone[p];
5855       PetscInt       dof;
5856 
5857       if ((cp < pStart) || (cp >= pEnd)) continue;
5858       PetscCall(PetscSectionGetDof(section, cp, &dof));
5859       size += dof;
5860     }
5861     if (!values) {
5862       if (csize) *csize = size;
5863       PetscFunctionReturn(PETSC_SUCCESS);
5864     }
5865     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5866   } else {
5867     array = *values;
5868   }
5869   size = 0;
5870   PetscCall(VecGetArrayRead(v, &vArray));
5871   if ((point >= pStart) && (point < pEnd)) {
5872     PetscInt           dof, off, d;
5873     const PetscScalar *varr;
5874 
5875     PetscCall(PetscSectionGetDof(section, point, &dof));
5876     PetscCall(PetscSectionGetOffset(section, point, &off));
5877     varr = &vArray[off];
5878     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5879     size += dof;
5880   }
5881   for (p = 0; p < numPoints; ++p) {
5882     const PetscInt     cp = cone[p];
5883     PetscInt           o  = coneO[p];
5884     PetscInt           dof, off, d;
5885     const PetscScalar *varr;
5886 
5887     if ((cp < pStart) || (cp >= pEnd)) continue;
5888     PetscCall(PetscSectionGetDof(section, cp, &dof));
5889     PetscCall(PetscSectionGetOffset(section, cp, &off));
5890     varr = &vArray[off];
5891     if (o >= 0) {
5892       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5893     } else {
5894       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5895     }
5896     size += dof;
5897   }
5898   PetscCall(VecRestoreArrayRead(v, &vArray));
5899   if (!*values) {
5900     if (csize) *csize = size;
5901     *values = array;
5902   } else {
5903     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5904     *csize = size;
5905   }
5906   PetscFunctionReturn(PETSC_SUCCESS);
5907 }
5908 
5909 /* Compress out points not in the section */
5910 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5911 {
5912   const PetscInt np = *numPoints;
5913   PetscInt       pStart, pEnd, p, q;
5914 
5915   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5916   for (p = 0, q = 0; p < np; ++p) {
5917     const PetscInt r = points[p * 2];
5918     if ((r >= pStart) && (r < pEnd)) {
5919       points[q * 2]     = r;
5920       points[q * 2 + 1] = points[p * 2 + 1];
5921       ++q;
5922     }
5923   }
5924   *numPoints = q;
5925   return PETSC_SUCCESS;
5926 }
5927 
5928 /* Compressed closure does not apply closure permutation */
5929 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5930 {
5931   const PetscInt *cla = NULL;
5932   PetscInt        np, *pts = NULL;
5933 
5934   PetscFunctionBeginHot;
5935   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5936   if (!ornt && *clPoints) {
5937     PetscInt dof, off;
5938 
5939     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5940     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5941     PetscCall(ISGetIndices(*clPoints, &cla));
5942     np  = dof / 2;
5943     pts = (PetscInt *)&cla[off];
5944   } else {
5945     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5946     PetscCall(CompressPoints_Private(section, &np, pts));
5947   }
5948   *numPoints = np;
5949   *points    = pts;
5950   *clp       = cla;
5951   PetscFunctionReturn(PETSC_SUCCESS);
5952 }
5953 
5954 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5955 {
5956   PetscFunctionBeginHot;
5957   if (!*clPoints) {
5958     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5959   } else {
5960     PetscCall(ISRestoreIndices(*clPoints, clp));
5961   }
5962   *numPoints = 0;
5963   *points    = NULL;
5964   *clSec     = NULL;
5965   *clPoints  = NULL;
5966   *clp       = NULL;
5967   PetscFunctionReturn(PETSC_SUCCESS);
5968 }
5969 
5970 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5971 {
5972   PetscInt            offset = 0, p;
5973   const PetscInt    **perms  = NULL;
5974   const PetscScalar **flips  = NULL;
5975 
5976   PetscFunctionBeginHot;
5977   *size = 0;
5978   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5979   for (p = 0; p < numPoints; p++) {
5980     const PetscInt     point = points[2 * p];
5981     const PetscInt    *perm  = perms ? perms[p] : NULL;
5982     const PetscScalar *flip  = flips ? flips[p] : NULL;
5983     PetscInt           dof, off, d;
5984     const PetscScalar *varr;
5985 
5986     PetscCall(PetscSectionGetDof(section, point, &dof));
5987     PetscCall(PetscSectionGetOffset(section, point, &off));
5988     varr = &vArray[off];
5989     if (clperm) {
5990       if (perm) {
5991         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5992       } else {
5993         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5994       }
5995       if (flip) {
5996         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5997       }
5998     } else {
5999       if (perm) {
6000         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6001       } else {
6002         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6003       }
6004       if (flip) {
6005         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6006       }
6007     }
6008     offset += dof;
6009   }
6010   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6011   *size = offset;
6012   PetscFunctionReturn(PETSC_SUCCESS);
6013 }
6014 
6015 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[])
6016 {
6017   PetscInt offset = 0, f;
6018 
6019   PetscFunctionBeginHot;
6020   *size = 0;
6021   for (f = 0; f < numFields; ++f) {
6022     PetscInt            p;
6023     const PetscInt    **perms = NULL;
6024     const PetscScalar **flips = NULL;
6025 
6026     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6027     for (p = 0; p < numPoints; p++) {
6028       const PetscInt     point = points[2 * p];
6029       PetscInt           fdof, foff, b;
6030       const PetscScalar *varr;
6031       const PetscInt    *perm = perms ? perms[p] : NULL;
6032       const PetscScalar *flip = flips ? flips[p] : NULL;
6033 
6034       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6035       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6036       varr = &vArray[foff];
6037       if (clperm) {
6038         if (perm) {
6039           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6040         } else {
6041           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6042         }
6043         if (flip) {
6044           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6045         }
6046       } else {
6047         if (perm) {
6048           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6049         } else {
6050           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6051         }
6052         if (flip) {
6053           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6054         }
6055       }
6056       offset += fdof;
6057     }
6058     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6059   }
6060   *size = offset;
6061   PetscFunctionReturn(PETSC_SUCCESS);
6062 }
6063 
6064 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6065 {
6066   PetscSection    clSection;
6067   IS              clPoints;
6068   PetscInt       *points = NULL;
6069   const PetscInt *clp, *perm = NULL;
6070   PetscInt        depth, numFields, numPoints, asize;
6071 
6072   PetscFunctionBeginHot;
6073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6074   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6075   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6076   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6077   PetscCall(DMPlexGetDepth(dm, &depth));
6078   PetscCall(PetscSectionGetNumFields(section, &numFields));
6079   if (depth == 1 && numFields < 2) {
6080     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6081     PetscFunctionReturn(PETSC_SUCCESS);
6082   }
6083   /* Get points */
6084   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6085   /* Get sizes */
6086   asize = 0;
6087   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6088     PetscInt dof;
6089     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6090     asize += dof;
6091   }
6092   if (values) {
6093     const PetscScalar *vArray;
6094     PetscInt           size;
6095 
6096     if (*values) {
6097       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);
6098     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6099     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6100     PetscCall(VecGetArrayRead(v, &vArray));
6101     /* Get values */
6102     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6103     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6104     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6105     /* Cleanup array */
6106     PetscCall(VecRestoreArrayRead(v, &vArray));
6107   }
6108   if (csize) *csize = asize;
6109   /* Cleanup points */
6110   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6111   PetscFunctionReturn(PETSC_SUCCESS);
6112 }
6113 
6114 /*@C
6115   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6116 
6117   Not collective
6118 
6119   Input Parameters:
6120 + dm      - The `DM`
6121 . section - The section describing the layout in `v`, or `NULL` to use the default section
6122 . v       - The local vector
6123 - point   - The point in the `DM`
6124 
6125   Input/Output Parameters:
6126 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6127 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6128            if the user provided `NULL`, it is a borrowed array and should not be freed
6129 
6130   Level: intermediate
6131 
6132   Notes:
6133   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6134   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6135   assembly function, and a user may already have allocated storage for this operation.
6136 
6137   A typical use could be
6138 .vb
6139    values = NULL;
6140    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6141    for (cl = 0; cl < clSize; ++cl) {
6142      <Compute on closure>
6143    }
6144    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6145 .ve
6146   or
6147 .vb
6148    PetscMalloc1(clMaxSize, &values);
6149    for (p = pStart; p < pEnd; ++p) {
6150      clSize = clMaxSize;
6151      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6152      for (cl = 0; cl < clSize; ++cl) {
6153        <Compute on closure>
6154      }
6155    }
6156    PetscFree(values);
6157 .ve
6158 
6159   Fortran Notes:
6160   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6161 
6162 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6163 @*/
6164 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6165 {
6166   PetscFunctionBeginHot;
6167   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6168   PetscFunctionReturn(PETSC_SUCCESS);
6169 }
6170 
6171 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6172 {
6173   DMLabel            depthLabel;
6174   PetscSection       clSection;
6175   IS                 clPoints;
6176   PetscScalar       *array;
6177   const PetscScalar *vArray;
6178   PetscInt          *points = NULL;
6179   const PetscInt    *clp, *perm = NULL;
6180   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6181 
6182   PetscFunctionBeginHot;
6183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6184   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6185   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6186   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6187   PetscCall(DMPlexGetDepth(dm, &mdepth));
6188   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6189   PetscCall(PetscSectionGetNumFields(section, &numFields));
6190   if (mdepth == 1 && numFields < 2) {
6191     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6192     PetscFunctionReturn(PETSC_SUCCESS);
6193   }
6194   /* Get points */
6195   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6196   for (clsize = 0, p = 0; p < Np; p++) {
6197     PetscInt dof;
6198     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6199     clsize += dof;
6200   }
6201   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6202   /* Filter points */
6203   for (p = 0; p < numPoints * 2; p += 2) {
6204     PetscInt dep;
6205 
6206     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6207     if (dep != depth) continue;
6208     points[Np * 2 + 0] = points[p];
6209     points[Np * 2 + 1] = points[p + 1];
6210     ++Np;
6211   }
6212   /* Get array */
6213   if (!values || !*values) {
6214     PetscInt asize = 0, dof;
6215 
6216     for (p = 0; p < Np * 2; p += 2) {
6217       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6218       asize += dof;
6219     }
6220     if (!values) {
6221       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6222       if (csize) *csize = asize;
6223       PetscFunctionReturn(PETSC_SUCCESS);
6224     }
6225     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6226   } else {
6227     array = *values;
6228   }
6229   PetscCall(VecGetArrayRead(v, &vArray));
6230   /* Get values */
6231   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6232   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6233   /* Cleanup points */
6234   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6235   /* Cleanup array */
6236   PetscCall(VecRestoreArrayRead(v, &vArray));
6237   if (!*values) {
6238     if (csize) *csize = size;
6239     *values = array;
6240   } else {
6241     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6242     *csize = size;
6243   }
6244   PetscFunctionReturn(PETSC_SUCCESS);
6245 }
6246 
6247 /*@C
6248   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6249 
6250   Not collective
6251 
6252   Input Parameters:
6253 + dm      - The `DM`
6254 . section - The section describing the layout in `v`, or `NULL` to use the default section
6255 . v       - The local vector
6256 . point   - The point in the `DM`
6257 . csize   - The number of values in the closure, or `NULL`
6258 - values  - The array of values, which is a borrowed array and should not be freed
6259 
6260   Level: intermediate
6261 
6262   Note:
6263   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6264 
6265   Fortran Notes:
6266   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6267 
6268 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6269 @*/
6270 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6271 {
6272   PetscInt size = 0;
6273 
6274   PetscFunctionBegin;
6275   /* Should work without recalculating size */
6276   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6277   *values = NULL;
6278   PetscFunctionReturn(PETSC_SUCCESS);
6279 }
6280 
6281 static inline void add(PetscScalar *x, PetscScalar y)
6282 {
6283   *x += y;
6284 }
6285 static inline void insert(PetscScalar *x, PetscScalar y)
6286 {
6287   *x = y;
6288 }
6289 
6290 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[])
6291 {
6292   PetscInt        cdof;  /* The number of constraints on this point */
6293   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6294   PetscScalar    *a;
6295   PetscInt        off, cind = 0, k;
6296 
6297   PetscFunctionBegin;
6298   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6299   PetscCall(PetscSectionGetOffset(section, point, &off));
6300   a = &array[off];
6301   if (!cdof || setBC) {
6302     if (clperm) {
6303       if (perm) {
6304         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6305       } else {
6306         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6307       }
6308     } else {
6309       if (perm) {
6310         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6311       } else {
6312         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6313       }
6314     }
6315   } else {
6316     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6317     if (clperm) {
6318       if (perm) {
6319         for (k = 0; k < dof; ++k) {
6320           if ((cind < cdof) && (k == cdofs[cind])) {
6321             ++cind;
6322             continue;
6323           }
6324           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6325         }
6326       } else {
6327         for (k = 0; k < dof; ++k) {
6328           if ((cind < cdof) && (k == cdofs[cind])) {
6329             ++cind;
6330             continue;
6331           }
6332           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6333         }
6334       }
6335     } else {
6336       if (perm) {
6337         for (k = 0; k < dof; ++k) {
6338           if ((cind < cdof) && (k == cdofs[cind])) {
6339             ++cind;
6340             continue;
6341           }
6342           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6343         }
6344       } else {
6345         for (k = 0; k < dof; ++k) {
6346           if ((cind < cdof) && (k == cdofs[cind])) {
6347             ++cind;
6348             continue;
6349           }
6350           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6351         }
6352       }
6353     }
6354   }
6355   PetscFunctionReturn(PETSC_SUCCESS);
6356 }
6357 
6358 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[])
6359 {
6360   PetscInt        cdof;  /* The number of constraints on this point */
6361   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6362   PetscScalar    *a;
6363   PetscInt        off, cind = 0, k;
6364 
6365   PetscFunctionBegin;
6366   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6367   PetscCall(PetscSectionGetOffset(section, point, &off));
6368   a = &array[off];
6369   if (cdof) {
6370     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6371     if (clperm) {
6372       if (perm) {
6373         for (k = 0; k < dof; ++k) {
6374           if ((cind < cdof) && (k == cdofs[cind])) {
6375             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6376             cind++;
6377           }
6378         }
6379       } else {
6380         for (k = 0; k < dof; ++k) {
6381           if ((cind < cdof) && (k == cdofs[cind])) {
6382             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6383             cind++;
6384           }
6385         }
6386       }
6387     } else {
6388       if (perm) {
6389         for (k = 0; k < dof; ++k) {
6390           if ((cind < cdof) && (k == cdofs[cind])) {
6391             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6392             cind++;
6393           }
6394         }
6395       } else {
6396         for (k = 0; k < dof; ++k) {
6397           if ((cind < cdof) && (k == cdofs[cind])) {
6398             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6399             cind++;
6400           }
6401         }
6402       }
6403     }
6404   }
6405   PetscFunctionReturn(PETSC_SUCCESS);
6406 }
6407 
6408 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[])
6409 {
6410   PetscScalar    *a;
6411   PetscInt        fdof, foff, fcdof, foffset = *offset;
6412   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6413   PetscInt        cind = 0, b;
6414 
6415   PetscFunctionBegin;
6416   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6417   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6418   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6419   a = &array[foff];
6420   if (!fcdof || setBC) {
6421     if (clperm) {
6422       if (perm) {
6423         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6424       } else {
6425         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6426       }
6427     } else {
6428       if (perm) {
6429         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6430       } else {
6431         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6432       }
6433     }
6434   } else {
6435     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6436     if (clperm) {
6437       if (perm) {
6438         for (b = 0; b < fdof; b++) {
6439           if ((cind < fcdof) && (b == fcdofs[cind])) {
6440             ++cind;
6441             continue;
6442           }
6443           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6444         }
6445       } else {
6446         for (b = 0; b < fdof; b++) {
6447           if ((cind < fcdof) && (b == fcdofs[cind])) {
6448             ++cind;
6449             continue;
6450           }
6451           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6452         }
6453       }
6454     } else {
6455       if (perm) {
6456         for (b = 0; b < fdof; b++) {
6457           if ((cind < fcdof) && (b == fcdofs[cind])) {
6458             ++cind;
6459             continue;
6460           }
6461           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6462         }
6463       } else {
6464         for (b = 0; b < fdof; b++) {
6465           if ((cind < fcdof) && (b == fcdofs[cind])) {
6466             ++cind;
6467             continue;
6468           }
6469           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6470         }
6471       }
6472     }
6473   }
6474   *offset += fdof;
6475   PetscFunctionReturn(PETSC_SUCCESS);
6476 }
6477 
6478 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[])
6479 {
6480   PetscScalar    *a;
6481   PetscInt        fdof, foff, fcdof, foffset = *offset;
6482   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6483   PetscInt        Nc, cind = 0, ncind = 0, b;
6484   PetscBool       ncSet, fcSet;
6485 
6486   PetscFunctionBegin;
6487   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6488   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6489   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6490   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6491   a = &array[foff];
6492   if (fcdof) {
6493     /* We just override fcdof and fcdofs with Ncc and comps */
6494     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6495     if (clperm) {
6496       if (perm) {
6497         if (comps) {
6498           for (b = 0; b < fdof; b++) {
6499             ncSet = fcSet = PETSC_FALSE;
6500             if (b % Nc == comps[ncind]) {
6501               ncind = (ncind + 1) % Ncc;
6502               ncSet = PETSC_TRUE;
6503             }
6504             if ((cind < fcdof) && (b == fcdofs[cind])) {
6505               ++cind;
6506               fcSet = PETSC_TRUE;
6507             }
6508             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6509           }
6510         } else {
6511           for (b = 0; b < fdof; b++) {
6512             if ((cind < fcdof) && (b == fcdofs[cind])) {
6513               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6514               ++cind;
6515             }
6516           }
6517         }
6518       } else {
6519         if (comps) {
6520           for (b = 0; b < fdof; b++) {
6521             ncSet = fcSet = PETSC_FALSE;
6522             if (b % Nc == comps[ncind]) {
6523               ncind = (ncind + 1) % Ncc;
6524               ncSet = PETSC_TRUE;
6525             }
6526             if ((cind < fcdof) && (b == fcdofs[cind])) {
6527               ++cind;
6528               fcSet = PETSC_TRUE;
6529             }
6530             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6531           }
6532         } else {
6533           for (b = 0; b < fdof; b++) {
6534             if ((cind < fcdof) && (b == fcdofs[cind])) {
6535               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6536               ++cind;
6537             }
6538           }
6539         }
6540       }
6541     } else {
6542       if (perm) {
6543         if (comps) {
6544           for (b = 0; b < fdof; b++) {
6545             ncSet = fcSet = PETSC_FALSE;
6546             if (b % Nc == comps[ncind]) {
6547               ncind = (ncind + 1) % Ncc;
6548               ncSet = PETSC_TRUE;
6549             }
6550             if ((cind < fcdof) && (b == fcdofs[cind])) {
6551               ++cind;
6552               fcSet = PETSC_TRUE;
6553             }
6554             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6555           }
6556         } else {
6557           for (b = 0; b < fdof; b++) {
6558             if ((cind < fcdof) && (b == fcdofs[cind])) {
6559               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6560               ++cind;
6561             }
6562           }
6563         }
6564       } else {
6565         if (comps) {
6566           for (b = 0; b < fdof; b++) {
6567             ncSet = fcSet = PETSC_FALSE;
6568             if (b % Nc == comps[ncind]) {
6569               ncind = (ncind + 1) % Ncc;
6570               ncSet = PETSC_TRUE;
6571             }
6572             if ((cind < fcdof) && (b == fcdofs[cind])) {
6573               ++cind;
6574               fcSet = PETSC_TRUE;
6575             }
6576             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6577           }
6578         } else {
6579           for (b = 0; b < fdof; b++) {
6580             if ((cind < fcdof) && (b == fcdofs[cind])) {
6581               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6582               ++cind;
6583             }
6584           }
6585         }
6586       }
6587     }
6588   }
6589   *offset += fdof;
6590   PetscFunctionReturn(PETSC_SUCCESS);
6591 }
6592 
6593 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6594 {
6595   PetscScalar    *array;
6596   const PetscInt *cone, *coneO;
6597   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6598 
6599   PetscFunctionBeginHot;
6600   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6601   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6602   PetscCall(DMPlexGetCone(dm, point, &cone));
6603   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6604   PetscCall(VecGetArray(v, &array));
6605   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6606     const PetscInt cp = !p ? point : cone[p - 1];
6607     const PetscInt o  = !p ? 0 : coneO[p - 1];
6608 
6609     if ((cp < pStart) || (cp >= pEnd)) {
6610       dof = 0;
6611       continue;
6612     }
6613     PetscCall(PetscSectionGetDof(section, cp, &dof));
6614     /* ADD_VALUES */
6615     {
6616       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6617       PetscScalar    *a;
6618       PetscInt        cdof, coff, cind = 0, k;
6619 
6620       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6621       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6622       a = &array[coff];
6623       if (!cdof) {
6624         if (o >= 0) {
6625           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6626         } else {
6627           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6628         }
6629       } else {
6630         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6631         if (o >= 0) {
6632           for (k = 0; k < dof; ++k) {
6633             if ((cind < cdof) && (k == cdofs[cind])) {
6634               ++cind;
6635               continue;
6636             }
6637             a[k] += values[off + k];
6638           }
6639         } else {
6640           for (k = 0; k < dof; ++k) {
6641             if ((cind < cdof) && (k == cdofs[cind])) {
6642               ++cind;
6643               continue;
6644             }
6645             a[k] += values[off + dof - k - 1];
6646           }
6647         }
6648       }
6649     }
6650   }
6651   PetscCall(VecRestoreArray(v, &array));
6652   PetscFunctionReturn(PETSC_SUCCESS);
6653 }
6654 
6655 /*@C
6656   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6657 
6658   Not collective
6659 
6660   Input Parameters:
6661 + dm      - The `DM`
6662 . section - The section describing the layout in `v`, or `NULL` to use the default section
6663 . v       - The local vector
6664 . point   - The point in the `DM`
6665 . values  - The array of values
6666 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6667          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6668 
6669   Level: intermediate
6670 
6671 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6672 @*/
6673 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6674 {
6675   PetscSection    clSection;
6676   IS              clPoints;
6677   PetscScalar    *array;
6678   PetscInt       *points = NULL;
6679   const PetscInt *clp, *clperm = NULL;
6680   PetscInt        depth, numFields, numPoints, p, clsize;
6681 
6682   PetscFunctionBeginHot;
6683   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6684   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6685   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6686   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6687   PetscCall(DMPlexGetDepth(dm, &depth));
6688   PetscCall(PetscSectionGetNumFields(section, &numFields));
6689   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6690     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6691     PetscFunctionReturn(PETSC_SUCCESS);
6692   }
6693   /* Get points */
6694   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6695   for (clsize = 0, p = 0; p < numPoints; p++) {
6696     PetscInt dof;
6697     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6698     clsize += dof;
6699   }
6700   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6701   /* Get array */
6702   PetscCall(VecGetArray(v, &array));
6703   /* Get values */
6704   if (numFields > 0) {
6705     PetscInt offset = 0, f;
6706     for (f = 0; f < numFields; ++f) {
6707       const PetscInt    **perms = NULL;
6708       const PetscScalar **flips = NULL;
6709 
6710       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6711       switch (mode) {
6712       case INSERT_VALUES:
6713         for (p = 0; p < numPoints; p++) {
6714           const PetscInt     point = points[2 * p];
6715           const PetscInt    *perm  = perms ? perms[p] : NULL;
6716           const PetscScalar *flip  = flips ? flips[p] : NULL;
6717           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6718         }
6719         break;
6720       case INSERT_ALL_VALUES:
6721         for (p = 0; p < numPoints; p++) {
6722           const PetscInt     point = points[2 * p];
6723           const PetscInt    *perm  = perms ? perms[p] : NULL;
6724           const PetscScalar *flip  = flips ? flips[p] : NULL;
6725           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6726         }
6727         break;
6728       case INSERT_BC_VALUES:
6729         for (p = 0; p < numPoints; p++) {
6730           const PetscInt     point = points[2 * p];
6731           const PetscInt    *perm  = perms ? perms[p] : NULL;
6732           const PetscScalar *flip  = flips ? flips[p] : NULL;
6733           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6734         }
6735         break;
6736       case ADD_VALUES:
6737         for (p = 0; p < numPoints; p++) {
6738           const PetscInt     point = points[2 * p];
6739           const PetscInt    *perm  = perms ? perms[p] : NULL;
6740           const PetscScalar *flip  = flips ? flips[p] : NULL;
6741           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6742         }
6743         break;
6744       case ADD_ALL_VALUES:
6745         for (p = 0; p < numPoints; p++) {
6746           const PetscInt     point = points[2 * p];
6747           const PetscInt    *perm  = perms ? perms[p] : NULL;
6748           const PetscScalar *flip  = flips ? flips[p] : NULL;
6749           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6750         }
6751         break;
6752       case ADD_BC_VALUES:
6753         for (p = 0; p < numPoints; p++) {
6754           const PetscInt     point = points[2 * p];
6755           const PetscInt    *perm  = perms ? perms[p] : NULL;
6756           const PetscScalar *flip  = flips ? flips[p] : NULL;
6757           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6758         }
6759         break;
6760       default:
6761         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6762       }
6763       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6764     }
6765   } else {
6766     PetscInt            dof, off;
6767     const PetscInt    **perms = NULL;
6768     const PetscScalar **flips = NULL;
6769 
6770     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6771     switch (mode) {
6772     case INSERT_VALUES:
6773       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6774         const PetscInt     point = points[2 * p];
6775         const PetscInt    *perm  = perms ? perms[p] : NULL;
6776         const PetscScalar *flip  = flips ? flips[p] : NULL;
6777         PetscCall(PetscSectionGetDof(section, point, &dof));
6778         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6779       }
6780       break;
6781     case INSERT_ALL_VALUES:
6782       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6783         const PetscInt     point = points[2 * p];
6784         const PetscInt    *perm  = perms ? perms[p] : NULL;
6785         const PetscScalar *flip  = flips ? flips[p] : NULL;
6786         PetscCall(PetscSectionGetDof(section, point, &dof));
6787         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6788       }
6789       break;
6790     case INSERT_BC_VALUES:
6791       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6792         const PetscInt     point = points[2 * p];
6793         const PetscInt    *perm  = perms ? perms[p] : NULL;
6794         const PetscScalar *flip  = flips ? flips[p] : NULL;
6795         PetscCall(PetscSectionGetDof(section, point, &dof));
6796         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6797       }
6798       break;
6799     case ADD_VALUES:
6800       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6801         const PetscInt     point = points[2 * p];
6802         const PetscInt    *perm  = perms ? perms[p] : NULL;
6803         const PetscScalar *flip  = flips ? flips[p] : NULL;
6804         PetscCall(PetscSectionGetDof(section, point, &dof));
6805         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6806       }
6807       break;
6808     case ADD_ALL_VALUES:
6809       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6810         const PetscInt     point = points[2 * p];
6811         const PetscInt    *perm  = perms ? perms[p] : NULL;
6812         const PetscScalar *flip  = flips ? flips[p] : NULL;
6813         PetscCall(PetscSectionGetDof(section, point, &dof));
6814         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6815       }
6816       break;
6817     case ADD_BC_VALUES:
6818       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6819         const PetscInt     point = points[2 * p];
6820         const PetscInt    *perm  = perms ? perms[p] : NULL;
6821         const PetscScalar *flip  = flips ? flips[p] : NULL;
6822         PetscCall(PetscSectionGetDof(section, point, &dof));
6823         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6824       }
6825       break;
6826     default:
6827       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6828     }
6829     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6830   }
6831   /* Cleanup points */
6832   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6833   /* Cleanup array */
6834   PetscCall(VecRestoreArray(v, &array));
6835   PetscFunctionReturn(PETSC_SUCCESS);
6836 }
6837 
6838 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6839 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6840 {
6841   PetscFunctionBegin;
6842   *contains = PETSC_TRUE;
6843   if (label) {
6844     PetscInt fdof;
6845 
6846     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6847     if (!*contains) {
6848       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6849       *offset += fdof;
6850       PetscFunctionReturn(PETSC_SUCCESS);
6851     }
6852   }
6853   PetscFunctionReturn(PETSC_SUCCESS);
6854 }
6855 
6856 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6857 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)
6858 {
6859   PetscSection    clSection;
6860   IS              clPoints;
6861   PetscScalar    *array;
6862   PetscInt       *points = NULL;
6863   const PetscInt *clp;
6864   PetscInt        numFields, numPoints, p;
6865   PetscInt        offset = 0, f;
6866 
6867   PetscFunctionBeginHot;
6868   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6869   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6870   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6871   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6872   PetscCall(PetscSectionGetNumFields(section, &numFields));
6873   /* Get points */
6874   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6875   /* Get array */
6876   PetscCall(VecGetArray(v, &array));
6877   /* Get values */
6878   for (f = 0; f < numFields; ++f) {
6879     const PetscInt    **perms = NULL;
6880     const PetscScalar **flips = NULL;
6881     PetscBool           contains;
6882 
6883     if (!fieldActive[f]) {
6884       for (p = 0; p < numPoints * 2; p += 2) {
6885         PetscInt fdof;
6886         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6887         offset += fdof;
6888       }
6889       continue;
6890     }
6891     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6892     switch (mode) {
6893     case INSERT_VALUES:
6894       for (p = 0; p < numPoints; p++) {
6895         const PetscInt     point = points[2 * p];
6896         const PetscInt    *perm  = perms ? perms[p] : NULL;
6897         const PetscScalar *flip  = flips ? flips[p] : NULL;
6898         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6899         if (!contains) continue;
6900         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6901       }
6902       break;
6903     case INSERT_ALL_VALUES:
6904       for (p = 0; p < numPoints; p++) {
6905         const PetscInt     point = points[2 * p];
6906         const PetscInt    *perm  = perms ? perms[p] : NULL;
6907         const PetscScalar *flip  = flips ? flips[p] : NULL;
6908         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6909         if (!contains) continue;
6910         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6911       }
6912       break;
6913     case INSERT_BC_VALUES:
6914       for (p = 0; p < numPoints; p++) {
6915         const PetscInt     point = points[2 * p];
6916         const PetscInt    *perm  = perms ? perms[p] : NULL;
6917         const PetscScalar *flip  = flips ? flips[p] : NULL;
6918         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6919         if (!contains) continue;
6920         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6921       }
6922       break;
6923     case ADD_VALUES:
6924       for (p = 0; p < numPoints; p++) {
6925         const PetscInt     point = points[2 * p];
6926         const PetscInt    *perm  = perms ? perms[p] : NULL;
6927         const PetscScalar *flip  = flips ? flips[p] : NULL;
6928         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6929         if (!contains) continue;
6930         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6931       }
6932       break;
6933     case ADD_ALL_VALUES:
6934       for (p = 0; p < numPoints; p++) {
6935         const PetscInt     point = points[2 * p];
6936         const PetscInt    *perm  = perms ? perms[p] : NULL;
6937         const PetscScalar *flip  = flips ? flips[p] : NULL;
6938         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6939         if (!contains) continue;
6940         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6941       }
6942       break;
6943     default:
6944       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6945     }
6946     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6947   }
6948   /* Cleanup points */
6949   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6950   /* Cleanup array */
6951   PetscCall(VecRestoreArray(v, &array));
6952   PetscFunctionReturn(PETSC_SUCCESS);
6953 }
6954 
6955 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6956 {
6957   PetscMPIInt rank;
6958   PetscInt    i, j;
6959 
6960   PetscFunctionBegin;
6961   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6962   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6963   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6964   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6965   numCIndices = numCIndices ? numCIndices : numRIndices;
6966   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6967   for (i = 0; i < numRIndices; i++) {
6968     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6969     for (j = 0; j < numCIndices; j++) {
6970 #if defined(PETSC_USE_COMPLEX)
6971       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6972 #else
6973       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6974 #endif
6975     }
6976     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6977   }
6978   PetscFunctionReturn(PETSC_SUCCESS);
6979 }
6980 
6981 /*
6982   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6983 
6984   Input Parameters:
6985 + section - The section for this data layout
6986 . islocal - Is the section (and thus indices being requested) local or global?
6987 . point   - The point contributing dofs with these indices
6988 . off     - The global offset of this point
6989 . loff    - The local offset of each field
6990 . setBC   - The flag determining whether to include indices of boundary values
6991 . perm    - A permutation of the dofs on this point, or NULL
6992 - indperm - A permutation of the entire indices array, or NULL
6993 
6994   Output Parameter:
6995 . indices - Indices for dofs on this point
6996 
6997   Level: developer
6998 
6999   Note: The indices could be local or global, depending on the value of 'off'.
7000 */
7001 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7002 {
7003   PetscInt        dof;   /* The number of unknowns on this point */
7004   PetscInt        cdof;  /* The number of constraints on this point */
7005   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7006   PetscInt        cind = 0, k;
7007 
7008   PetscFunctionBegin;
7009   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7010   PetscCall(PetscSectionGetDof(section, point, &dof));
7011   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7012   if (!cdof || setBC) {
7013     for (k = 0; k < dof; ++k) {
7014       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7015       const PetscInt ind    = indperm ? indperm[preind] : preind;
7016 
7017       indices[ind] = off + k;
7018     }
7019   } else {
7020     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7021     for (k = 0; k < dof; ++k) {
7022       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7023       const PetscInt ind    = indperm ? indperm[preind] : preind;
7024 
7025       if ((cind < cdof) && (k == cdofs[cind])) {
7026         /* Insert check for returning constrained indices */
7027         indices[ind] = -(off + k + 1);
7028         ++cind;
7029       } else {
7030         indices[ind] = off + k - (islocal ? 0 : cind);
7031       }
7032     }
7033   }
7034   *loff += dof;
7035   PetscFunctionReturn(PETSC_SUCCESS);
7036 }
7037 
7038 /*
7039  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7040 
7041  Input Parameters:
7042 + section - a section (global or local)
7043 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7044 . point - point within section
7045 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7046 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7047 . setBC - identify constrained (boundary condition) points via involution.
7048 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7049 . permsoff - offset
7050 - indperm - index permutation
7051 
7052  Output Parameter:
7053 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7054 . indices - array to hold indices (as defined by section) of each dof associated with point
7055 
7056  Notes:
7057  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7058  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7059  in the local vector.
7060 
7061  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7062  significant).  It is invalid to call with a global section and setBC=true.
7063 
7064  Developer Note:
7065  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7066  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7067  offset could be obtained from the section instead of passing it explicitly as we do now.
7068 
7069  Example:
7070  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7071  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7072  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7073  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.
7074 
7075  Level: developer
7076 */
7077 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[])
7078 {
7079   PetscInt numFields, foff, f;
7080 
7081   PetscFunctionBegin;
7082   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7083   PetscCall(PetscSectionGetNumFields(section, &numFields));
7084   for (f = 0, foff = 0; f < numFields; ++f) {
7085     PetscInt        fdof, cfdof;
7086     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7087     PetscInt        cind = 0, b;
7088     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7089 
7090     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7091     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7092     if (!cfdof || setBC) {
7093       for (b = 0; b < fdof; ++b) {
7094         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7095         const PetscInt ind    = indperm ? indperm[preind] : preind;
7096 
7097         indices[ind] = off + foff + b;
7098       }
7099     } else {
7100       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7101       for (b = 0; b < fdof; ++b) {
7102         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7103         const PetscInt ind    = indperm ? indperm[preind] : preind;
7104 
7105         if ((cind < cfdof) && (b == fcdofs[cind])) {
7106           indices[ind] = -(off + foff + b + 1);
7107           ++cind;
7108         } else {
7109           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7110         }
7111       }
7112     }
7113     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7114     foffs[f] += fdof;
7115   }
7116   PetscFunctionReturn(PETSC_SUCCESS);
7117 }
7118 
7119 /*
7120   This version believes the globalSection offsets for each field, rather than just the point offset
7121 
7122  . foffs - The offset into 'indices' for each field, since it is segregated by field
7123 
7124  Notes:
7125  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7126  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7127 */
7128 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7129 {
7130   PetscInt numFields, foff, f;
7131 
7132   PetscFunctionBegin;
7133   PetscCall(PetscSectionGetNumFields(section, &numFields));
7134   for (f = 0; f < numFields; ++f) {
7135     PetscInt        fdof, cfdof;
7136     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7137     PetscInt        cind = 0, b;
7138     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7139 
7140     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7141     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7142     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7143     if (!cfdof) {
7144       for (b = 0; b < fdof; ++b) {
7145         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7146         const PetscInt ind    = indperm ? indperm[preind] : preind;
7147 
7148         indices[ind] = foff + b;
7149       }
7150     } else {
7151       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7152       for (b = 0; b < fdof; ++b) {
7153         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7154         const PetscInt ind    = indperm ? indperm[preind] : preind;
7155 
7156         if ((cind < cfdof) && (b == fcdofs[cind])) {
7157           indices[ind] = -(foff + b + 1);
7158           ++cind;
7159         } else {
7160           indices[ind] = foff + b - cind;
7161         }
7162       }
7163     }
7164     foffs[f] += fdof;
7165   }
7166   PetscFunctionReturn(PETSC_SUCCESS);
7167 }
7168 
7169 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)
7170 {
7171   Mat             cMat;
7172   PetscSection    aSec, cSec;
7173   IS              aIS;
7174   PetscInt        aStart = -1, aEnd = -1;
7175   const PetscInt *anchors;
7176   PetscInt        numFields, f, p, q, newP = 0;
7177   PetscInt        newNumPoints = 0, newNumIndices = 0;
7178   PetscInt       *newPoints, *indices, *newIndices;
7179   PetscInt        maxAnchor, maxDof;
7180   PetscInt        newOffsets[32];
7181   PetscInt       *pointMatOffsets[32];
7182   PetscInt       *newPointOffsets[32];
7183   PetscScalar    *pointMat[32];
7184   PetscScalar    *newValues      = NULL, *tmpValues;
7185   PetscBool       anyConstrained = PETSC_FALSE;
7186 
7187   PetscFunctionBegin;
7188   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7189   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7190   PetscCall(PetscSectionGetNumFields(section, &numFields));
7191 
7192   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7193   /* if there are point-to-point constraints */
7194   if (aSec) {
7195     PetscCall(PetscArrayzero(newOffsets, 32));
7196     PetscCall(ISGetIndices(aIS, &anchors));
7197     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7198     /* figure out how many points are going to be in the new element matrix
7199      * (we allow double counting, because it's all just going to be summed
7200      * into the global matrix anyway) */
7201     for (p = 0; p < 2 * numPoints; p += 2) {
7202       PetscInt b    = points[p];
7203       PetscInt bDof = 0, bSecDof;
7204 
7205       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7206       if (!bSecDof) continue;
7207       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7208       if (bDof) {
7209         /* this point is constrained */
7210         /* it is going to be replaced by its anchors */
7211         PetscInt bOff, q;
7212 
7213         anyConstrained = PETSC_TRUE;
7214         newNumPoints += bDof;
7215         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7216         for (q = 0; q < bDof; q++) {
7217           PetscInt a = anchors[bOff + q];
7218           PetscInt aDof;
7219 
7220           PetscCall(PetscSectionGetDof(section, a, &aDof));
7221           newNumIndices += aDof;
7222           for (f = 0; f < numFields; ++f) {
7223             PetscInt fDof;
7224 
7225             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7226             newOffsets[f + 1] += fDof;
7227           }
7228         }
7229       } else {
7230         /* this point is not constrained */
7231         newNumPoints++;
7232         newNumIndices += bSecDof;
7233         for (f = 0; f < numFields; ++f) {
7234           PetscInt fDof;
7235 
7236           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7237           newOffsets[f + 1] += fDof;
7238         }
7239       }
7240     }
7241   }
7242   if (!anyConstrained) {
7243     if (outNumPoints) *outNumPoints = 0;
7244     if (outNumIndices) *outNumIndices = 0;
7245     if (outPoints) *outPoints = NULL;
7246     if (outValues) *outValues = NULL;
7247     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7248     PetscFunctionReturn(PETSC_SUCCESS);
7249   }
7250 
7251   if (outNumPoints) *outNumPoints = newNumPoints;
7252   if (outNumIndices) *outNumIndices = newNumIndices;
7253 
7254   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7255 
7256   if (!outPoints && !outValues) {
7257     if (offsets) {
7258       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7259     }
7260     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7261     PetscFunctionReturn(PETSC_SUCCESS);
7262   }
7263 
7264   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7265 
7266   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7267 
7268   /* workspaces */
7269   if (numFields) {
7270     for (f = 0; f < numFields; f++) {
7271       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7272       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7273     }
7274   } else {
7275     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7276     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7277   }
7278 
7279   /* get workspaces for the point-to-point matrices */
7280   if (numFields) {
7281     PetscInt totalOffset, totalMatOffset;
7282 
7283     for (p = 0; p < numPoints; p++) {
7284       PetscInt b    = points[2 * p];
7285       PetscInt bDof = 0, bSecDof;
7286 
7287       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7288       if (!bSecDof) {
7289         for (f = 0; f < numFields; f++) {
7290           newPointOffsets[f][p + 1] = 0;
7291           pointMatOffsets[f][p + 1] = 0;
7292         }
7293         continue;
7294       }
7295       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7296       if (bDof) {
7297         for (f = 0; f < numFields; f++) {
7298           PetscInt fDof, q, bOff, allFDof = 0;
7299 
7300           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7301           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7302           for (q = 0; q < bDof; q++) {
7303             PetscInt a = anchors[bOff + q];
7304             PetscInt aFDof;
7305 
7306             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7307             allFDof += aFDof;
7308           }
7309           newPointOffsets[f][p + 1] = allFDof;
7310           pointMatOffsets[f][p + 1] = fDof * allFDof;
7311         }
7312       } else {
7313         for (f = 0; f < numFields; f++) {
7314           PetscInt fDof;
7315 
7316           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7317           newPointOffsets[f][p + 1] = fDof;
7318           pointMatOffsets[f][p + 1] = 0;
7319         }
7320       }
7321     }
7322     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7323       newPointOffsets[f][0] = totalOffset;
7324       pointMatOffsets[f][0] = totalMatOffset;
7325       for (p = 0; p < numPoints; p++) {
7326         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7327         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7328       }
7329       totalOffset    = newPointOffsets[f][numPoints];
7330       totalMatOffset = pointMatOffsets[f][numPoints];
7331       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7332     }
7333   } else {
7334     for (p = 0; p < numPoints; p++) {
7335       PetscInt b    = points[2 * p];
7336       PetscInt bDof = 0, bSecDof;
7337 
7338       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7339       if (!bSecDof) {
7340         newPointOffsets[0][p + 1] = 0;
7341         pointMatOffsets[0][p + 1] = 0;
7342         continue;
7343       }
7344       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7345       if (bDof) {
7346         PetscInt bOff, q, allDof = 0;
7347 
7348         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7349         for (q = 0; q < bDof; q++) {
7350           PetscInt a = anchors[bOff + q], aDof;
7351 
7352           PetscCall(PetscSectionGetDof(section, a, &aDof));
7353           allDof += aDof;
7354         }
7355         newPointOffsets[0][p + 1] = allDof;
7356         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7357       } else {
7358         newPointOffsets[0][p + 1] = bSecDof;
7359         pointMatOffsets[0][p + 1] = 0;
7360       }
7361     }
7362     newPointOffsets[0][0] = 0;
7363     pointMatOffsets[0][0] = 0;
7364     for (p = 0; p < numPoints; p++) {
7365       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7366       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7367     }
7368     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7369   }
7370 
7371   /* output arrays */
7372   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7373 
7374   /* get the point-to-point matrices; construct newPoints */
7375   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7376   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7377   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7378   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7379   if (numFields) {
7380     for (p = 0, newP = 0; p < numPoints; p++) {
7381       PetscInt b    = points[2 * p];
7382       PetscInt o    = points[2 * p + 1];
7383       PetscInt bDof = 0, bSecDof;
7384 
7385       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7386       if (!bSecDof) continue;
7387       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7388       if (bDof) {
7389         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7390 
7391         fStart[0] = 0;
7392         fEnd[0]   = 0;
7393         for (f = 0; f < numFields; f++) {
7394           PetscInt fDof;
7395 
7396           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7397           fStart[f + 1] = fStart[f] + fDof;
7398           fEnd[f + 1]   = fStart[f + 1];
7399         }
7400         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7401         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7402 
7403         fAnchorStart[0] = 0;
7404         fAnchorEnd[0]   = 0;
7405         for (f = 0; f < numFields; f++) {
7406           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7407 
7408           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7409           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7410         }
7411         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7412         for (q = 0; q < bDof; q++) {
7413           PetscInt a = anchors[bOff + q], aOff;
7414 
7415           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7416           newPoints[2 * (newP + q)]     = a;
7417           newPoints[2 * (newP + q) + 1] = 0;
7418           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7419           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7420         }
7421         newP += bDof;
7422 
7423         if (outValues) {
7424           /* get the point-to-point submatrix */
7425           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]));
7426         }
7427       } else {
7428         newPoints[2 * newP]     = b;
7429         newPoints[2 * newP + 1] = o;
7430         newP++;
7431       }
7432     }
7433   } else {
7434     for (p = 0; p < numPoints; p++) {
7435       PetscInt b    = points[2 * p];
7436       PetscInt o    = points[2 * p + 1];
7437       PetscInt bDof = 0, bSecDof;
7438 
7439       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7440       if (!bSecDof) continue;
7441       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7442       if (bDof) {
7443         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7444 
7445         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7446         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7447 
7448         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7449         for (q = 0; q < bDof; q++) {
7450           PetscInt a = anchors[bOff + q], aOff;
7451 
7452           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7453 
7454           newPoints[2 * (newP + q)]     = a;
7455           newPoints[2 * (newP + q) + 1] = 0;
7456           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7457           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7458         }
7459         newP += bDof;
7460 
7461         /* get the point-to-point submatrix */
7462         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7463       } else {
7464         newPoints[2 * newP]     = b;
7465         newPoints[2 * newP + 1] = o;
7466         newP++;
7467       }
7468     }
7469   }
7470 
7471   if (outValues) {
7472     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7473     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7474     /* multiply constraints on the right */
7475     if (numFields) {
7476       for (f = 0; f < numFields; f++) {
7477         PetscInt oldOff = offsets[f];
7478 
7479         for (p = 0; p < numPoints; p++) {
7480           PetscInt cStart = newPointOffsets[f][p];
7481           PetscInt b      = points[2 * p];
7482           PetscInt c, r, k;
7483           PetscInt dof;
7484 
7485           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7486           if (!dof) continue;
7487           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7488             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7489             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7490 
7491             for (r = 0; r < numIndices; r++) {
7492               for (c = 0; c < nCols; c++) {
7493                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7494               }
7495             }
7496           } else {
7497             /* copy this column as is */
7498             for (r = 0; r < numIndices; r++) {
7499               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7500             }
7501           }
7502           oldOff += dof;
7503         }
7504       }
7505     } else {
7506       PetscInt oldOff = 0;
7507       for (p = 0; p < numPoints; p++) {
7508         PetscInt cStart = newPointOffsets[0][p];
7509         PetscInt b      = points[2 * p];
7510         PetscInt c, r, k;
7511         PetscInt dof;
7512 
7513         PetscCall(PetscSectionGetDof(section, b, &dof));
7514         if (!dof) continue;
7515         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7516           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7517           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7518 
7519           for (r = 0; r < numIndices; r++) {
7520             for (c = 0; c < nCols; c++) {
7521               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7522             }
7523           }
7524         } else {
7525           /* copy this column as is */
7526           for (r = 0; r < numIndices; r++) {
7527             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7528           }
7529         }
7530         oldOff += dof;
7531       }
7532     }
7533 
7534     if (multiplyLeft) {
7535       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7536       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7537       /* multiply constraints transpose on the left */
7538       if (numFields) {
7539         for (f = 0; f < numFields; f++) {
7540           PetscInt oldOff = offsets[f];
7541 
7542           for (p = 0; p < numPoints; p++) {
7543             PetscInt rStart = newPointOffsets[f][p];
7544             PetscInt b      = points[2 * p];
7545             PetscInt c, r, k;
7546             PetscInt dof;
7547 
7548             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7549             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7550               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7551               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7552 
7553               for (r = 0; r < nRows; r++) {
7554                 for (c = 0; c < newNumIndices; c++) {
7555                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7556                 }
7557               }
7558             } else {
7559               /* copy this row as is */
7560               for (r = 0; r < dof; r++) {
7561                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7562               }
7563             }
7564             oldOff += dof;
7565           }
7566         }
7567       } else {
7568         PetscInt oldOff = 0;
7569 
7570         for (p = 0; p < numPoints; p++) {
7571           PetscInt rStart = newPointOffsets[0][p];
7572           PetscInt b      = points[2 * p];
7573           PetscInt c, r, k;
7574           PetscInt dof;
7575 
7576           PetscCall(PetscSectionGetDof(section, b, &dof));
7577           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7578             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7579             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7580 
7581             for (r = 0; r < nRows; r++) {
7582               for (c = 0; c < newNumIndices; c++) {
7583                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7584               }
7585             }
7586           } else {
7587             /* copy this row as is */
7588             for (r = 0; r < dof; r++) {
7589               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7590             }
7591           }
7592           oldOff += dof;
7593         }
7594       }
7595 
7596       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7597     } else {
7598       newValues = tmpValues;
7599     }
7600   }
7601 
7602   /* clean up */
7603   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7604   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7605 
7606   if (numFields) {
7607     for (f = 0; f < numFields; f++) {
7608       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7609       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7610       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7611     }
7612   } else {
7613     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7614     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7615     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7616   }
7617   PetscCall(ISRestoreIndices(aIS, &anchors));
7618 
7619   /* output */
7620   if (outPoints) {
7621     *outPoints = newPoints;
7622   } else {
7623     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7624   }
7625   if (outValues) *outValues = newValues;
7626   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7627   PetscFunctionReturn(PETSC_SUCCESS);
7628 }
7629 
7630 /*@C
7631   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7632 
7633   Not collective
7634 
7635   Input Parameters:
7636 + dm         - The `DM`
7637 . section    - The `PetscSection` describing the points (a local section)
7638 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7639 . point      - The point defining the closure
7640 - useClPerm  - Use the closure point permutation if available
7641 
7642   Output Parameters:
7643 + numIndices - The number of dof indices in the closure of point with the input sections
7644 . indices    - The dof indices
7645 . outOffsets - Array to write the field offsets into, or `NULL`
7646 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7647 
7648   Level: advanced
7649 
7650   Notes:
7651   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7652 
7653   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7654   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7655   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7656   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7657   indices (with the above semantics) are implied.
7658 
7659 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7660           `PetscSection`, `DMGetGlobalSection()`
7661 @*/
7662 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7663 {
7664   /* Closure ordering */
7665   PetscSection    clSection;
7666   IS              clPoints;
7667   const PetscInt *clp;
7668   PetscInt       *points;
7669   const PetscInt *clperm = NULL;
7670   /* Dof permutation and sign flips */
7671   const PetscInt    **perms[32] = {NULL};
7672   const PetscScalar **flips[32] = {NULL};
7673   PetscScalar        *valCopy   = NULL;
7674   /* Hanging node constraints */
7675   PetscInt    *pointsC = NULL;
7676   PetscScalar *valuesC = NULL;
7677   PetscInt     NclC, NiC;
7678 
7679   PetscInt *idx;
7680   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7681   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7682 
7683   PetscFunctionBeginHot;
7684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7685   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7686   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7687   if (numIndices) PetscAssertPointer(numIndices, 6);
7688   if (indices) PetscAssertPointer(indices, 7);
7689   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7690   if (values) PetscAssertPointer(values, 9);
7691   PetscCall(PetscSectionGetNumFields(section, &Nf));
7692   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7693   PetscCall(PetscArrayzero(offsets, 32));
7694   /* 1) Get points in closure */
7695   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7696   if (useClPerm) {
7697     PetscInt depth, clsize;
7698     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7699     for (clsize = 0, p = 0; p < Ncl; p++) {
7700       PetscInt dof;
7701       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7702       clsize += dof;
7703     }
7704     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7705   }
7706   /* 2) Get number of indices on these points and field offsets from section */
7707   for (p = 0; p < Ncl * 2; p += 2) {
7708     PetscInt dof, fdof;
7709 
7710     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7711     for (f = 0; f < Nf; ++f) {
7712       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7713       offsets[f + 1] += fdof;
7714     }
7715     Ni += dof;
7716   }
7717   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7718   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7719   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7720   for (f = 0; f < PetscMax(1, Nf); ++f) {
7721     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7722     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7723     /* may need to apply sign changes to the element matrix */
7724     if (values && flips[f]) {
7725       PetscInt foffset = offsets[f];
7726 
7727       for (p = 0; p < Ncl; ++p) {
7728         PetscInt           pnt  = points[2 * p], fdof;
7729         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7730 
7731         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7732         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7733         if (flip) {
7734           PetscInt i, j, k;
7735 
7736           if (!valCopy) {
7737             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7738             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7739             *values = valCopy;
7740           }
7741           for (i = 0; i < fdof; ++i) {
7742             PetscScalar fval = flip[i];
7743 
7744             for (k = 0; k < Ni; ++k) {
7745               valCopy[Ni * (foffset + i) + k] *= fval;
7746               valCopy[Ni * k + (foffset + i)] *= fval;
7747             }
7748           }
7749         }
7750         foffset += fdof;
7751       }
7752     }
7753   }
7754   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7755   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7756   if (NclC) {
7757     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7758     for (f = 0; f < PetscMax(1, Nf); ++f) {
7759       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7760       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7761     }
7762     for (f = 0; f < PetscMax(1, Nf); ++f) {
7763       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7764       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7765     }
7766     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7767     Ncl    = NclC;
7768     Ni     = NiC;
7769     points = pointsC;
7770     if (values) *values = valuesC;
7771   }
7772   /* 5) Calculate indices */
7773   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7774   if (Nf) {
7775     PetscInt  idxOff;
7776     PetscBool useFieldOffsets;
7777 
7778     if (outOffsets) {
7779       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7780     }
7781     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7782     if (useFieldOffsets) {
7783       for (p = 0; p < Ncl; ++p) {
7784         const PetscInt pnt = points[p * 2];
7785 
7786         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7787       }
7788     } else {
7789       for (p = 0; p < Ncl; ++p) {
7790         const PetscInt pnt = points[p * 2];
7791 
7792         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7793         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7794          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7795          * global section. */
7796         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7797       }
7798     }
7799   } else {
7800     PetscInt off = 0, idxOff;
7801 
7802     for (p = 0; p < Ncl; ++p) {
7803       const PetscInt  pnt  = points[p * 2];
7804       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7805 
7806       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7807       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7808        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7809       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7810     }
7811   }
7812   /* 6) Cleanup */
7813   for (f = 0; f < PetscMax(1, Nf); ++f) {
7814     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7815     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7816   }
7817   if (NclC) {
7818     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7819   } else {
7820     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7821   }
7822 
7823   if (numIndices) *numIndices = Ni;
7824   if (indices) *indices = idx;
7825   PetscFunctionReturn(PETSC_SUCCESS);
7826 }
7827 
7828 /*@C
7829   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7830 
7831   Not collective
7832 
7833   Input Parameters:
7834 + dm         - The `DM`
7835 . section    - The `PetscSection` describing the points (a local section)
7836 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7837 . point      - The point defining the closure
7838 - useClPerm  - Use the closure point permutation if available
7839 
7840   Output Parameters:
7841 + numIndices - The number of dof indices in the closure of point with the input sections
7842 . indices    - The dof indices
7843 . outOffsets - Array to write the field offsets into, or `NULL`
7844 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7845 
7846   Level: advanced
7847 
7848   Notes:
7849   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7850 
7851   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7852   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7853   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7854   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7855   indices (with the above semantics) are implied.
7856 
7857 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7858 @*/
7859 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7860 {
7861   PetscFunctionBegin;
7862   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7863   PetscAssertPointer(indices, 7);
7864   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7865   PetscFunctionReturn(PETSC_SUCCESS);
7866 }
7867 
7868 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7869 {
7870   DM_Plex           *mesh = (DM_Plex *)dm->data;
7871   PetscInt          *indices;
7872   PetscInt           numIndices;
7873   const PetscScalar *valuesOrig = values;
7874   PetscErrorCode     ierr;
7875 
7876   PetscFunctionBegin;
7877   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7878   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7879   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7880   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7881   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7882   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
7883 
7884   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
7885 
7886   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7887   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7888   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7889   if (ierr) {
7890     PetscMPIInt rank;
7891 
7892     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7893     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7894     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7895     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7896     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7897     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7898   }
7899   if (mesh->printFEM > 1) {
7900     PetscInt i;
7901     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7902     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7903     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7904   }
7905 
7906   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7907   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7908   PetscFunctionReturn(PETSC_SUCCESS);
7909 }
7910 
7911 /*@C
7912   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7913 
7914   Not collective
7915 
7916   Input Parameters:
7917 + dm            - The `DM`
7918 . section       - The section describing the layout in `v`, or `NULL` to use the default section
7919 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7920 . A             - The matrix
7921 . point         - The point in the `DM`
7922 . values        - The array of values
7923 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7924 
7925   Level: intermediate
7926 
7927 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7928 @*/
7929 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7930 {
7931   PetscFunctionBegin;
7932   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
7933   PetscFunctionReturn(PETSC_SUCCESS);
7934 }
7935 
7936 /*@C
7937   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
7938 
7939   Not collective
7940 
7941   Input Parameters:
7942 + dmRow            - The `DM` for the row fields
7943 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
7944 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
7945 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7946 . dmCol            - The `DM` for the column fields
7947 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
7948 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
7949 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7950 . A                - The matrix
7951 . point            - The point in the `DM`
7952 . values           - The array of values
7953 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7954 
7955   Level: intermediate
7956 
7957 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7958 @*/
7959 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, PetscBool useRowPerm, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, PetscBool useColPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7960 {
7961   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7962   PetscInt          *indicesRow, *indicesCol;
7963   PetscInt           numIndicesRow, numIndicesCol;
7964   const PetscScalar *valuesOrig = values;
7965   PetscErrorCode     ierr;
7966 
7967   PetscFunctionBegin;
7968   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7969   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7970   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7971   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7972   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7973   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
7974   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7975   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
7976   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7977   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
7978   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
7979 
7980   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7981   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7982 
7983   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7984   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7985   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7986   if (ierr) {
7987     PetscMPIInt rank;
7988 
7989     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7990     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7991     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7992     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7993     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7994     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7995   }
7996 
7997   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7998   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7999   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8000   PetscFunctionReturn(PETSC_SUCCESS);
8001 }
8002 
8003 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8004 {
8005   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8006   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8007   PetscInt       *cpoints = NULL;
8008   PetscInt       *findices, *cindices;
8009   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8010   PetscInt        foffsets[32], coffsets[32];
8011   DMPolytopeType  ct;
8012   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8013   PetscErrorCode  ierr;
8014 
8015   PetscFunctionBegin;
8016   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8017   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8018   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8019   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8020   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8021   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8022   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8023   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8024   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8025   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8026   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8027   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8028   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8029   PetscCall(PetscArrayzero(foffsets, 32));
8030   PetscCall(PetscArrayzero(coffsets, 32));
8031   /* Column indices */
8032   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8033   maxFPoints = numCPoints;
8034   /* Compress out points not in the section */
8035   /*   TODO: Squeeze out points with 0 dof as well */
8036   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8037   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8038     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8039       cpoints[q * 2]     = cpoints[p];
8040       cpoints[q * 2 + 1] = cpoints[p + 1];
8041       ++q;
8042     }
8043   }
8044   numCPoints = q;
8045   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8046     PetscInt fdof;
8047 
8048     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8049     if (!dof) continue;
8050     for (f = 0; f < numFields; ++f) {
8051       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8052       coffsets[f + 1] += fdof;
8053     }
8054     numCIndices += dof;
8055   }
8056   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8057   /* Row indices */
8058   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8059   {
8060     DMPlexTransform tr;
8061     DMPolytopeType *rct;
8062     PetscInt       *rsize, *rcone, *rornt, Nt;
8063 
8064     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8065     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8066     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8067     numSubcells = rsize[Nt - 1];
8068     PetscCall(DMPlexTransformDestroy(&tr));
8069   }
8070   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8071   for (r = 0, q = 0; r < numSubcells; ++r) {
8072     /* TODO Map from coarse to fine cells */
8073     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8074     /* Compress out points not in the section */
8075     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8076     for (p = 0; p < numFPoints * 2; p += 2) {
8077       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8078         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8079         if (!dof) continue;
8080         for (s = 0; s < q; ++s)
8081           if (fpoints[p] == ftotpoints[s * 2]) break;
8082         if (s < q) continue;
8083         ftotpoints[q * 2]     = fpoints[p];
8084         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8085         ++q;
8086       }
8087     }
8088     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8089   }
8090   numFPoints = q;
8091   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8092     PetscInt fdof;
8093 
8094     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8095     if (!dof) continue;
8096     for (f = 0; f < numFields; ++f) {
8097       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8098       foffsets[f + 1] += fdof;
8099     }
8100     numFIndices += dof;
8101   }
8102   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8103 
8104   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8105   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8106   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8107   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8108   if (numFields) {
8109     const PetscInt **permsF[32] = {NULL};
8110     const PetscInt **permsC[32] = {NULL};
8111 
8112     for (f = 0; f < numFields; f++) {
8113       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8114       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8115     }
8116     for (p = 0; p < numFPoints; p++) {
8117       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8118       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8119     }
8120     for (p = 0; p < numCPoints; p++) {
8121       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8122       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8123     }
8124     for (f = 0; f < numFields; f++) {
8125       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8126       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8127     }
8128   } else {
8129     const PetscInt **permsF = NULL;
8130     const PetscInt **permsC = NULL;
8131 
8132     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8133     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8134     for (p = 0, off = 0; p < numFPoints; p++) {
8135       const PetscInt *perm = permsF ? permsF[p] : NULL;
8136 
8137       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8138       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8139     }
8140     for (p = 0, off = 0; p < numCPoints; p++) {
8141       const PetscInt *perm = permsC ? permsC[p] : NULL;
8142 
8143       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8144       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8145     }
8146     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8147     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8148   }
8149   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8150   /* TODO: flips */
8151   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8152   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8153   if (ierr) {
8154     PetscMPIInt rank;
8155 
8156     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8157     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8158     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8159     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8160     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8161   }
8162   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8163   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8164   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8165   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8166   PetscFunctionReturn(PETSC_SUCCESS);
8167 }
8168 
8169 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8170 {
8171   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8172   PetscInt       *cpoints = NULL;
8173   PetscInt        foffsets[32], coffsets[32];
8174   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8175   DMPolytopeType  ct;
8176   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8177 
8178   PetscFunctionBegin;
8179   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8180   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8181   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8182   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8183   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8184   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8185   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8186   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8187   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8188   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8189   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8190   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8191   PetscCall(PetscArrayzero(foffsets, 32));
8192   PetscCall(PetscArrayzero(coffsets, 32));
8193   /* Column indices */
8194   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8195   maxFPoints = numCPoints;
8196   /* Compress out points not in the section */
8197   /*   TODO: Squeeze out points with 0 dof as well */
8198   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8199   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8200     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8201       cpoints[q * 2]     = cpoints[p];
8202       cpoints[q * 2 + 1] = cpoints[p + 1];
8203       ++q;
8204     }
8205   }
8206   numCPoints = q;
8207   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8208     PetscInt fdof;
8209 
8210     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8211     if (!dof) continue;
8212     for (f = 0; f < numFields; ++f) {
8213       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8214       coffsets[f + 1] += fdof;
8215     }
8216     numCIndices += dof;
8217   }
8218   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8219   /* Row indices */
8220   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8221   {
8222     DMPlexTransform tr;
8223     DMPolytopeType *rct;
8224     PetscInt       *rsize, *rcone, *rornt, Nt;
8225 
8226     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8227     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8228     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8229     numSubcells = rsize[Nt - 1];
8230     PetscCall(DMPlexTransformDestroy(&tr));
8231   }
8232   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8233   for (r = 0, q = 0; r < numSubcells; ++r) {
8234     /* TODO Map from coarse to fine cells */
8235     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8236     /* Compress out points not in the section */
8237     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8238     for (p = 0; p < numFPoints * 2; p += 2) {
8239       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8240         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8241         if (!dof) continue;
8242         for (s = 0; s < q; ++s)
8243           if (fpoints[p] == ftotpoints[s * 2]) break;
8244         if (s < q) continue;
8245         ftotpoints[q * 2]     = fpoints[p];
8246         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8247         ++q;
8248       }
8249     }
8250     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8251   }
8252   numFPoints = q;
8253   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8254     PetscInt fdof;
8255 
8256     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8257     if (!dof) continue;
8258     for (f = 0; f < numFields; ++f) {
8259       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8260       foffsets[f + 1] += fdof;
8261     }
8262     numFIndices += dof;
8263   }
8264   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8265 
8266   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8267   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8268   if (numFields) {
8269     const PetscInt **permsF[32] = {NULL};
8270     const PetscInt **permsC[32] = {NULL};
8271 
8272     for (f = 0; f < numFields; f++) {
8273       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8274       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8275     }
8276     for (p = 0; p < numFPoints; p++) {
8277       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8278       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8279     }
8280     for (p = 0; p < numCPoints; p++) {
8281       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8282       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8283     }
8284     for (f = 0; f < numFields; f++) {
8285       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8286       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8287     }
8288   } else {
8289     const PetscInt **permsF = NULL;
8290     const PetscInt **permsC = NULL;
8291 
8292     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8293     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8294     for (p = 0, off = 0; p < numFPoints; p++) {
8295       const PetscInt *perm = permsF ? permsF[p] : NULL;
8296 
8297       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8298       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8299     }
8300     for (p = 0, off = 0; p < numCPoints; p++) {
8301       const PetscInt *perm = permsC ? permsC[p] : NULL;
8302 
8303       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8304       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8305     }
8306     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8307     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8308   }
8309   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8310   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8311   PetscFunctionReturn(PETSC_SUCCESS);
8312 }
8313 
8314 /*@C
8315   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8316 
8317   Input Parameter:
8318 . dm - The `DMPLEX` object
8319 
8320   Output Parameter:
8321 . cellHeight - The height of a cell
8322 
8323   Level: developer
8324 
8325 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8326 @*/
8327 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8328 {
8329   DM_Plex *mesh = (DM_Plex *)dm->data;
8330 
8331   PetscFunctionBegin;
8332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8333   PetscAssertPointer(cellHeight, 2);
8334   *cellHeight = mesh->vtkCellHeight;
8335   PetscFunctionReturn(PETSC_SUCCESS);
8336 }
8337 
8338 /*@C
8339   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8340 
8341   Input Parameters:
8342 + dm         - The `DMPLEX` object
8343 - cellHeight - The height of a cell
8344 
8345   Level: developer
8346 
8347 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8348 @*/
8349 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8350 {
8351   DM_Plex *mesh = (DM_Plex *)dm->data;
8352 
8353   PetscFunctionBegin;
8354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8355   mesh->vtkCellHeight = cellHeight;
8356   PetscFunctionReturn(PETSC_SUCCESS);
8357 }
8358 
8359 /*@
8360   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8361 
8362   Input Parameters:
8363 + dm - The `DMPLEX` object
8364 - ct - The `DMPolytopeType` of the cell
8365 
8366   Output Parameters:
8367 + start - The first cell of this type, or `NULL`
8368 - end   - The upper bound on this celltype, or `NULL`
8369 
8370   Level: advanced
8371 
8372 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8373 @*/
8374 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8375 {
8376   DM_Plex *mesh = (DM_Plex *)dm->data;
8377   DMLabel  label;
8378   PetscInt pStart, pEnd;
8379 
8380   PetscFunctionBegin;
8381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8382   if (start) {
8383     PetscAssertPointer(start, 3);
8384     *start = 0;
8385   }
8386   if (end) {
8387     PetscAssertPointer(end, 4);
8388     *end = 0;
8389   }
8390   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8391   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8392   if (mesh->tr) {
8393     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8394   } else {
8395     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8396     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8397     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8398   }
8399   PetscFunctionReturn(PETSC_SUCCESS);
8400 }
8401 
8402 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8403 {
8404   PetscSection section, globalSection;
8405   PetscInt    *numbers, p;
8406 
8407   PetscFunctionBegin;
8408   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8409   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8410   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8411   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8412   PetscCall(PetscSectionSetUp(section));
8413   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8414   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8415   for (p = pStart; p < pEnd; ++p) {
8416     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8417     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8418     else numbers[p - pStart] += shift;
8419   }
8420   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8421   if (globalSize) {
8422     PetscLayout layout;
8423     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8424     PetscCall(PetscLayoutGetSize(layout, globalSize));
8425     PetscCall(PetscLayoutDestroy(&layout));
8426   }
8427   PetscCall(PetscSectionDestroy(&section));
8428   PetscCall(PetscSectionDestroy(&globalSection));
8429   PetscFunctionReturn(PETSC_SUCCESS);
8430 }
8431 
8432 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8433 {
8434   PetscInt cellHeight, cStart, cEnd;
8435 
8436   PetscFunctionBegin;
8437   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8438   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8439   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8440   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8441   PetscFunctionReturn(PETSC_SUCCESS);
8442 }
8443 
8444 /*@
8445   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8446 
8447   Input Parameter:
8448 . dm - The `DMPLEX` object
8449 
8450   Output Parameter:
8451 . globalCellNumbers - Global cell numbers for all cells on this process
8452 
8453   Level: developer
8454 
8455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8456 @*/
8457 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8458 {
8459   DM_Plex *mesh = (DM_Plex *)dm->data;
8460 
8461   PetscFunctionBegin;
8462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8463   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8464   *globalCellNumbers = mesh->globalCellNumbers;
8465   PetscFunctionReturn(PETSC_SUCCESS);
8466 }
8467 
8468 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8469 {
8470   PetscInt vStart, vEnd;
8471 
8472   PetscFunctionBegin;
8473   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8474   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8475   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8476   PetscFunctionReturn(PETSC_SUCCESS);
8477 }
8478 
8479 /*@
8480   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8481 
8482   Input Parameter:
8483 . dm - The `DMPLEX` object
8484 
8485   Output Parameter:
8486 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8487 
8488   Level: developer
8489 
8490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8491 @*/
8492 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8493 {
8494   DM_Plex *mesh = (DM_Plex *)dm->data;
8495 
8496   PetscFunctionBegin;
8497   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8498   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8499   *globalVertexNumbers = mesh->globalVertexNumbers;
8500   PetscFunctionReturn(PETSC_SUCCESS);
8501 }
8502 
8503 /*@
8504   DMPlexCreatePointNumbering - Create a global numbering for all points.
8505 
8506   Collective
8507 
8508   Input Parameter:
8509 . dm - The `DMPLEX` object
8510 
8511   Output Parameter:
8512 . globalPointNumbers - Global numbers for all points on this process
8513 
8514   Level: developer
8515 
8516   Notes:
8517   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8518   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8519   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8520   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8521 
8522   The partitioned mesh is
8523   ```
8524   (2)--0--(3)--1--(4)    (1)--0--(2)
8525   ```
8526   and its global numbering is
8527   ```
8528   (3)--0--(4)--1--(5)--2--(6)
8529   ```
8530   Then the global numbering is provided as
8531   ```
8532   [0] Number of indices in set 5
8533   [0] 0 0
8534   [0] 1 1
8535   [0] 2 3
8536   [0] 3 4
8537   [0] 4 -6
8538   [1] Number of indices in set 3
8539   [1] 0 2
8540   [1] 1 5
8541   [1] 2 6
8542   ```
8543 
8544 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8545 @*/
8546 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8547 {
8548   IS        nums[4];
8549   PetscInt  depths[4], gdepths[4], starts[4];
8550   PetscInt  depth, d, shift = 0;
8551   PetscBool empty = PETSC_FALSE;
8552 
8553   PetscFunctionBegin;
8554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8555   PetscCall(DMPlexGetDepth(dm, &depth));
8556   // For unstratified meshes use dim instead of depth
8557   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8558   // If any stratum is empty, we must mark all empty
8559   for (d = 0; d <= depth; ++d) {
8560     PetscInt end;
8561 
8562     depths[d] = depth - d;
8563     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8564     if (!(starts[d] - end)) empty = PETSC_TRUE;
8565   }
8566   if (empty)
8567     for (d = 0; d <= depth; ++d) {
8568       depths[d] = -1;
8569       starts[d] = -1;
8570     }
8571   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8572   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8573   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]);
8574   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8575   for (d = 0; d <= depth; ++d) {
8576     PetscInt pStart, pEnd, gsize;
8577 
8578     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8579     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8580     shift += gsize;
8581   }
8582   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8583   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8584   PetscFunctionReturn(PETSC_SUCCESS);
8585 }
8586 
8587 /*@
8588   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8589 
8590   Input Parameter:
8591 . dm - The `DMPLEX` object
8592 
8593   Output Parameter:
8594 . ranks - The rank field
8595 
8596   Options Database Key:
8597 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8598 
8599   Level: intermediate
8600 
8601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8602 @*/
8603 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8604 {
8605   DM             rdm;
8606   PetscFE        fe;
8607   PetscScalar   *r;
8608   PetscMPIInt    rank;
8609   DMPolytopeType ct;
8610   PetscInt       dim, cStart, cEnd, c;
8611   PetscBool      simplex;
8612 
8613   PetscFunctionBeginUser;
8614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8615   PetscAssertPointer(ranks, 2);
8616   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8617   PetscCall(DMClone(dm, &rdm));
8618   PetscCall(DMGetDimension(rdm, &dim));
8619   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8620   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8621   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8622   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8623   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8624   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8625   PetscCall(PetscFEDestroy(&fe));
8626   PetscCall(DMCreateDS(rdm));
8627   PetscCall(DMCreateGlobalVector(rdm, ranks));
8628   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8629   PetscCall(VecGetArray(*ranks, &r));
8630   for (c = cStart; c < cEnd; ++c) {
8631     PetscScalar *lr;
8632 
8633     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8634     if (lr) *lr = rank;
8635   }
8636   PetscCall(VecRestoreArray(*ranks, &r));
8637   PetscCall(DMDestroy(&rdm));
8638   PetscFunctionReturn(PETSC_SUCCESS);
8639 }
8640 
8641 /*@
8642   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8643 
8644   Input Parameters:
8645 + dm    - The `DMPLEX`
8646 - label - The `DMLabel`
8647 
8648   Output Parameter:
8649 . val - The label value field
8650 
8651   Options Database Key:
8652 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8653 
8654   Level: intermediate
8655 
8656 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8657 @*/
8658 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8659 {
8660   DM           rdm;
8661   PetscFE      fe;
8662   PetscScalar *v;
8663   PetscInt     dim, cStart, cEnd, c;
8664 
8665   PetscFunctionBeginUser;
8666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8667   PetscAssertPointer(label, 2);
8668   PetscAssertPointer(val, 3);
8669   PetscCall(DMClone(dm, &rdm));
8670   PetscCall(DMGetDimension(rdm, &dim));
8671   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8672   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8673   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8674   PetscCall(PetscFEDestroy(&fe));
8675   PetscCall(DMCreateDS(rdm));
8676   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8677   PetscCall(DMCreateGlobalVector(rdm, val));
8678   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8679   PetscCall(VecGetArray(*val, &v));
8680   for (c = cStart; c < cEnd; ++c) {
8681     PetscScalar *lv;
8682     PetscInt     cval;
8683 
8684     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8685     PetscCall(DMLabelGetValue(label, c, &cval));
8686     *lv = cval;
8687   }
8688   PetscCall(VecRestoreArray(*val, &v));
8689   PetscCall(DMDestroy(&rdm));
8690   PetscFunctionReturn(PETSC_SUCCESS);
8691 }
8692 
8693 /*@
8694   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8695 
8696   Input Parameter:
8697 . dm - The `DMPLEX` object
8698 
8699   Level: developer
8700 
8701   Notes:
8702   This is a useful diagnostic when creating meshes programmatically.
8703 
8704   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8705 
8706 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8707 @*/
8708 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8709 {
8710   PetscSection    coneSection, supportSection;
8711   const PetscInt *cone, *support;
8712   PetscInt        coneSize, c, supportSize, s;
8713   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8714   PetscBool       storagecheck = PETSC_TRUE;
8715 
8716   PetscFunctionBegin;
8717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8718   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8719   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8720   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8721   /* Check that point p is found in the support of its cone points, and vice versa */
8722   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8723   for (p = pStart; p < pEnd; ++p) {
8724     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8725     PetscCall(DMPlexGetCone(dm, p, &cone));
8726     for (c = 0; c < coneSize; ++c) {
8727       PetscBool dup = PETSC_FALSE;
8728       PetscInt  d;
8729       for (d = c - 1; d >= 0; --d) {
8730         if (cone[c] == cone[d]) {
8731           dup = PETSC_TRUE;
8732           break;
8733         }
8734       }
8735       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8736       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8737       for (s = 0; s < supportSize; ++s) {
8738         if (support[s] == p) break;
8739       }
8740       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8741         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8742         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8743         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8744         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8745         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8746         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8747         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]);
8748         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8749       }
8750     }
8751     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8752     if (p != pp) {
8753       storagecheck = PETSC_FALSE;
8754       continue;
8755     }
8756     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8757     PetscCall(DMPlexGetSupport(dm, p, &support));
8758     for (s = 0; s < supportSize; ++s) {
8759       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8760       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8761       for (c = 0; c < coneSize; ++c) {
8762         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8763         if (cone[c] != pp) {
8764           c = 0;
8765           break;
8766         }
8767         if (cone[c] == p) break;
8768       }
8769       if (c >= coneSize) {
8770         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8771         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8772         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8773         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8774         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8775         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8776         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8777       }
8778     }
8779   }
8780   if (storagecheck) {
8781     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8782     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8783     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8784   }
8785   PetscFunctionReturn(PETSC_SUCCESS);
8786 }
8787 
8788 /*
8789   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.
8790 */
8791 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8792 {
8793   DMPolytopeType  cct;
8794   PetscInt        ptpoints[4];
8795   const PetscInt *cone, *ccone, *ptcone;
8796   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8797 
8798   PetscFunctionBegin;
8799   *unsplit = 0;
8800   switch (ct) {
8801   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8802     ptpoints[npt++] = c;
8803     break;
8804   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8805     PetscCall(DMPlexGetCone(dm, c, &cone));
8806     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8807     for (cp = 0; cp < coneSize; ++cp) {
8808       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8809       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8810     }
8811     break;
8812   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8813   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8814     PetscCall(DMPlexGetCone(dm, c, &cone));
8815     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8816     for (cp = 0; cp < coneSize; ++cp) {
8817       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8818       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8819       for (ccp = 0; ccp < cconeSize; ++ccp) {
8820         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8821         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8822           PetscInt p;
8823           for (p = 0; p < npt; ++p)
8824             if (ptpoints[p] == ccone[ccp]) break;
8825           if (p == npt) ptpoints[npt++] = ccone[ccp];
8826         }
8827       }
8828     }
8829     break;
8830   default:
8831     break;
8832   }
8833   for (pt = 0; pt < npt; ++pt) {
8834     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8835     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8836   }
8837   PetscFunctionReturn(PETSC_SUCCESS);
8838 }
8839 
8840 /*@
8841   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8842 
8843   Input Parameters:
8844 + dm         - The `DMPLEX` object
8845 - cellHeight - Normally 0
8846 
8847   Level: developer
8848 
8849   Notes:
8850   This is a useful diagnostic when creating meshes programmatically.
8851   Currently applicable only to homogeneous simplex or tensor meshes.
8852 
8853   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8854 
8855 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8856 @*/
8857 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8858 {
8859   DMPlexInterpolatedFlag interp;
8860   DMPolytopeType         ct;
8861   PetscInt               vStart, vEnd, cStart, cEnd, c;
8862 
8863   PetscFunctionBegin;
8864   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8865   PetscCall(DMPlexIsInterpolated(dm, &interp));
8866   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8867   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8868   for (c = cStart; c < cEnd; ++c) {
8869     PetscInt *closure = NULL;
8870     PetscInt  coneSize, closureSize, cl, Nv = 0;
8871 
8872     PetscCall(DMPlexGetCellType(dm, c, &ct));
8873     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8874     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8875     if (interp == DMPLEX_INTERPOLATED_FULL) {
8876       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8877       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));
8878     }
8879     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8880     for (cl = 0; cl < closureSize * 2; cl += 2) {
8881       const PetscInt p = closure[cl];
8882       if ((p >= vStart) && (p < vEnd)) ++Nv;
8883     }
8884     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8885     /* Special Case: Tensor faces with identified vertices */
8886     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8887       PetscInt unsplit;
8888 
8889       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8890       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8891     }
8892     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));
8893   }
8894   PetscFunctionReturn(PETSC_SUCCESS);
8895 }
8896 
8897 /*@
8898   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8899 
8900   Collective
8901 
8902   Input Parameters:
8903 + dm         - The `DMPLEX` object
8904 - cellHeight - Normally 0
8905 
8906   Level: developer
8907 
8908   Notes:
8909   This is a useful diagnostic when creating meshes programmatically.
8910   This routine is only relevant for meshes that are fully interpolated across all ranks.
8911   It will error out if a partially interpolated mesh is given on some rank.
8912   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8913 
8914   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8915 
8916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8917 @*/
8918 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8919 {
8920   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8921   DMPlexInterpolatedFlag interpEnum;
8922 
8923   PetscFunctionBegin;
8924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8925   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8926   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8927   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8928     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8929     PetscFunctionReturn(PETSC_SUCCESS);
8930   }
8931 
8932   PetscCall(DMGetDimension(dm, &dim));
8933   PetscCall(DMPlexGetDepth(dm, &depth));
8934   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8935   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8936     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8937     for (c = cStart; c < cEnd; ++c) {
8938       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8939       const DMPolytopeType *faceTypes;
8940       DMPolytopeType        ct;
8941       PetscInt              numFaces, coneSize, f;
8942       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8943 
8944       PetscCall(DMPlexGetCellType(dm, c, &ct));
8945       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8946       if (unsplit) continue;
8947       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8948       PetscCall(DMPlexGetCone(dm, c, &cone));
8949       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8950       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8951       for (cl = 0; cl < closureSize * 2; cl += 2) {
8952         const PetscInt p = closure[cl];
8953         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8954       }
8955       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8956       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);
8957       for (f = 0; f < numFaces; ++f) {
8958         DMPolytopeType fct;
8959         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8960 
8961         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8962         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8963         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8964           const PetscInt p = fclosure[cl];
8965           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8966         }
8967         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]);
8968         for (v = 0; v < fnumCorners; ++v) {
8969           if (fclosure[v] != faces[fOff + v]) {
8970             PetscInt v1;
8971 
8972             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8973             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8974             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8975             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8976             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8977             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]);
8978           }
8979         }
8980         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8981         fOff += faceSizes[f];
8982       }
8983       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8984       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8985     }
8986   }
8987   PetscFunctionReturn(PETSC_SUCCESS);
8988 }
8989 
8990 /*@
8991   DMPlexCheckGeometry - Check the geometry of mesh cells
8992 
8993   Input Parameter:
8994 . dm - The `DMPLEX` object
8995 
8996   Level: developer
8997 
8998   Notes:
8999   This is a useful diagnostic when creating meshes programmatically.
9000 
9001   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9002 
9003 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9004 @*/
9005 PetscErrorCode DMPlexCheckGeometry(DM dm)
9006 {
9007   Vec       coordinates;
9008   PetscReal detJ, J[9], refVol = 1.0;
9009   PetscReal vol;
9010   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9011 
9012   PetscFunctionBegin;
9013   PetscCall(DMGetDimension(dm, &dim));
9014   PetscCall(DMGetCoordinateDim(dm, &dE));
9015   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9016   PetscCall(DMPlexGetDepth(dm, &depth));
9017   for (d = 0; d < dim; ++d) refVol *= 2.0;
9018   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9019   /* Make sure local coordinates are created, because that step is collective */
9020   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9021   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9022   for (c = cStart; c < cEnd; ++c) {
9023     DMPolytopeType ct;
9024     PetscInt       unsplit;
9025     PetscBool      ignoreZeroVol = PETSC_FALSE;
9026 
9027     PetscCall(DMPlexGetCellType(dm, c, &ct));
9028     switch (ct) {
9029     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9030     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9031     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9032       ignoreZeroVol = PETSC_TRUE;
9033       break;
9034     default:
9035       break;
9036     }
9037     switch (ct) {
9038     case DM_POLYTOPE_TRI_PRISM:
9039     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9040     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9041     case DM_POLYTOPE_PYRAMID:
9042       continue;
9043     default:
9044       break;
9045     }
9046     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9047     if (unsplit) continue;
9048     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9049     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);
9050     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9051     /* This should work with periodicity since DG coordinates should be used */
9052     if (depth > 1) {
9053       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9054       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);
9055       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9056     }
9057   }
9058   PetscFunctionReturn(PETSC_SUCCESS);
9059 }
9060 
9061 /*@
9062   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9063 
9064   Collective
9065 
9066   Input Parameters:
9067 + dm              - The `DMPLEX` object
9068 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9069 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9070 
9071   Level: developer
9072 
9073   Notes:
9074   This is mainly intended for debugging/testing purposes.
9075 
9076   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9077 
9078   Extra roots can come from periodic cuts, where additional points appear on the boundary
9079 
9080 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9081 @*/
9082 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9083 {
9084   PetscInt           l, nleaves, nroots, overlap;
9085   const PetscInt    *locals;
9086   const PetscSFNode *remotes;
9087   PetscBool          distributed;
9088   MPI_Comm           comm;
9089   PetscMPIInt        rank;
9090 
9091   PetscFunctionBegin;
9092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9093   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9094   else pointSF = dm->sf;
9095   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9096   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9097   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9098   {
9099     PetscMPIInt mpiFlag;
9100 
9101     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9102     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9103   }
9104   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9105   PetscCall(DMPlexIsDistributed(dm, &distributed));
9106   if (!distributed) {
9107     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);
9108     PetscFunctionReturn(PETSC_SUCCESS);
9109   }
9110   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);
9111   PetscCall(DMPlexGetOverlap(dm, &overlap));
9112 
9113   /* Check SF graph is compatible with DMPlex chart */
9114   {
9115     PetscInt pStart, pEnd, maxLeaf;
9116 
9117     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9118     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9119     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9120     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9121   }
9122 
9123   /* Check Point SF has no local points referenced */
9124   for (l = 0; l < nleaves; l++) {
9125     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);
9126   }
9127 
9128   /* Check there are no cells in interface */
9129   if (!overlap) {
9130     PetscInt cellHeight, cStart, cEnd;
9131 
9132     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9133     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9134     for (l = 0; l < nleaves; ++l) {
9135       const PetscInt point = locals ? locals[l] : l;
9136 
9137       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9138     }
9139   }
9140 
9141   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9142   {
9143     const PetscInt *rootdegree;
9144 
9145     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9146     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9147     for (l = 0; l < nleaves; ++l) {
9148       const PetscInt  point = locals ? locals[l] : l;
9149       const PetscInt *cone;
9150       PetscInt        coneSize, c, idx;
9151 
9152       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9153       PetscCall(DMPlexGetCone(dm, point, &cone));
9154       for (c = 0; c < coneSize; ++c) {
9155         if (!rootdegree[cone[c]]) {
9156           if (locals) {
9157             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9158           } else {
9159             idx = (cone[c] < nleaves) ? cone[c] : -1;
9160           }
9161           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9162         }
9163       }
9164     }
9165   }
9166   PetscFunctionReturn(PETSC_SUCCESS);
9167 }
9168 
9169 /*@
9170   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9171 
9172   Input Parameter:
9173 . dm - The `DMPLEX` object
9174 
9175   Level: developer
9176 
9177   Notes:
9178   This is a useful diagnostic when creating meshes programmatically.
9179 
9180   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9181 
9182   Currently does not include `DMPlexCheckCellShape()`.
9183 
9184 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9185 @*/
9186 PetscErrorCode DMPlexCheck(DM dm)
9187 {
9188   PetscInt cellHeight;
9189 
9190   PetscFunctionBegin;
9191   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9192   PetscCall(DMPlexCheckSymmetry(dm));
9193   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9194   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9195   PetscCall(DMPlexCheckGeometry(dm));
9196   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9197   PetscCall(DMPlexCheckInterfaceCones(dm));
9198   PetscFunctionReturn(PETSC_SUCCESS);
9199 }
9200 
9201 typedef struct cell_stats {
9202   PetscReal min, max, sum, squaresum;
9203   PetscInt  count;
9204 } cell_stats_t;
9205 
9206 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9207 {
9208   PetscInt i, N = *len;
9209 
9210   for (i = 0; i < N; i++) {
9211     cell_stats_t *A = (cell_stats_t *)a;
9212     cell_stats_t *B = (cell_stats_t *)b;
9213 
9214     B->min = PetscMin(A->min, B->min);
9215     B->max = PetscMax(A->max, B->max);
9216     B->sum += A->sum;
9217     B->squaresum += A->squaresum;
9218     B->count += A->count;
9219   }
9220 }
9221 
9222 /*@
9223   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9224 
9225   Collective
9226 
9227   Input Parameters:
9228 + dm        - The `DMPLEX` object
9229 . output    - If true, statistics will be displayed on `stdout`
9230 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9231 
9232   Level: developer
9233 
9234   Notes:
9235   This is mainly intended for debugging/testing purposes.
9236 
9237   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9238 
9239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9240 @*/
9241 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9242 {
9243   DM           dmCoarse;
9244   cell_stats_t stats, globalStats;
9245   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9246   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9247   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9248   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9249   PetscMPIInt  rank, size;
9250 
9251   PetscFunctionBegin;
9252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9253   stats.min = PETSC_MAX_REAL;
9254   stats.max = PETSC_MIN_REAL;
9255   stats.sum = stats.squaresum = 0.;
9256   stats.count                 = 0;
9257 
9258   PetscCallMPI(MPI_Comm_size(comm, &size));
9259   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9260   PetscCall(DMGetCoordinateDim(dm, &cdim));
9261   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9262   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9263   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9264   for (c = cStart; c < cEnd; c++) {
9265     PetscInt  i;
9266     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9267 
9268     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9269     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9270     for (i = 0; i < PetscSqr(cdim); ++i) {
9271       frobJ += J[i] * J[i];
9272       frobInvJ += invJ[i] * invJ[i];
9273     }
9274     cond2 = frobJ * frobInvJ;
9275     cond  = PetscSqrtReal(cond2);
9276 
9277     stats.min = PetscMin(stats.min, cond);
9278     stats.max = PetscMax(stats.max, cond);
9279     stats.sum += cond;
9280     stats.squaresum += cond2;
9281     stats.count++;
9282     if (output && cond > limit) {
9283       PetscSection coordSection;
9284       Vec          coordsLocal;
9285       PetscScalar *coords = NULL;
9286       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9287 
9288       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9289       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9290       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9291       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9292       for (i = 0; i < Nv / cdim; ++i) {
9293         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9294         for (d = 0; d < cdim; ++d) {
9295           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9296           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9297         }
9298         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9299       }
9300       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9301       for (cl = 0; cl < clSize * 2; cl += 2) {
9302         const PetscInt edge = closure[cl];
9303 
9304         if ((edge >= eStart) && (edge < eEnd)) {
9305           PetscReal len;
9306 
9307           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9308           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9309         }
9310       }
9311       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9312       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9313     }
9314   }
9315   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9316 
9317   if (size > 1) {
9318     PetscMPIInt  blockLengths[2] = {4, 1};
9319     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9320     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9321     MPI_Op       statReduce;
9322 
9323     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9324     PetscCallMPI(MPI_Type_commit(&statType));
9325     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9326     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9327     PetscCallMPI(MPI_Op_free(&statReduce));
9328     PetscCallMPI(MPI_Type_free(&statType));
9329   } else {
9330     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9331   }
9332   if (rank == 0) {
9333     count = globalStats.count;
9334     min   = globalStats.min;
9335     max   = globalStats.max;
9336     mean  = globalStats.sum / globalStats.count;
9337     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9338   }
9339 
9340   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));
9341   PetscCall(PetscFree2(J, invJ));
9342 
9343   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9344   if (dmCoarse) {
9345     PetscBool isplex;
9346 
9347     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9348     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9349   }
9350   PetscFunctionReturn(PETSC_SUCCESS);
9351 }
9352 
9353 /*@
9354   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9355   orthogonal quality below given tolerance.
9356 
9357   Collective
9358 
9359   Input Parameters:
9360 + dm   - The `DMPLEX` object
9361 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9362 - atol - [0, 1] Absolute tolerance for tagging cells.
9363 
9364   Output Parameters:
9365 + OrthQual      - `Vec` containing orthogonal quality per cell
9366 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9367 
9368   Options Database Keys:
9369 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9370 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9371 
9372   Level: intermediate
9373 
9374   Notes:
9375   Orthogonal quality is given by the following formula\:
9376 
9377   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9378 
9379   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
9380   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9381   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9382   calculating the cosine of the angle between these vectors.
9383 
9384   Orthogonal quality ranges from 1 (best) to 0 (worst).
9385 
9386   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9387   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9388 
9389   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9390 
9391 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9392 @*/
9393 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9394 {
9395   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9396   PetscInt              *idx;
9397   PetscScalar           *oqVals;
9398   const PetscScalar     *cellGeomArr, *faceGeomArr;
9399   PetscReal             *ci, *fi, *Ai;
9400   MPI_Comm               comm;
9401   Vec                    cellgeom, facegeom;
9402   DM                     dmFace, dmCell;
9403   IS                     glob;
9404   ISLocalToGlobalMapping ltog;
9405   PetscViewer            vwr;
9406 
9407   PetscFunctionBegin;
9408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9409   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9410   PetscAssertPointer(OrthQual, 4);
9411   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9412   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9413   PetscCall(DMGetDimension(dm, &nc));
9414   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9415   {
9416     DMPlexInterpolatedFlag interpFlag;
9417 
9418     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9419     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9420       PetscMPIInt rank;
9421 
9422       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9423       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9424     }
9425   }
9426   if (OrthQualLabel) {
9427     PetscAssertPointer(OrthQualLabel, 5);
9428     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9429     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9430   } else {
9431     *OrthQualLabel = NULL;
9432   }
9433   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9434   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9435   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9436   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9437   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9438   PetscCall(VecCreate(comm, OrthQual));
9439   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9440   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9441   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9442   PetscCall(VecSetUp(*OrthQual));
9443   PetscCall(ISDestroy(&glob));
9444   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9445   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9446   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9447   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9448   PetscCall(VecGetDM(cellgeom, &dmCell));
9449   PetscCall(VecGetDM(facegeom, &dmFace));
9450   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9451   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9452     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9453     PetscInt         cellarr[2], *adj = NULL;
9454     PetscScalar     *cArr, *fArr;
9455     PetscReal        minvalc = 1.0, minvalf = 1.0;
9456     PetscFVCellGeom *cg;
9457 
9458     idx[cellIter] = cell - cStart;
9459     cellarr[0]    = cell;
9460     /* Make indexing into cellGeom easier */
9461     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9462     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9463     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9464     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9465     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9466       PetscInt         i;
9467       const PetscInt   neigh  = adj[cellneigh];
9468       PetscReal        normci = 0, normfi = 0, normai = 0;
9469       PetscFVCellGeom *cgneigh;
9470       PetscFVFaceGeom *fg;
9471 
9472       /* Don't count ourselves in the neighbor list */
9473       if (neigh == cell) continue;
9474       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9475       cellarr[1] = neigh;
9476       {
9477         PetscInt        numcovpts;
9478         const PetscInt *covpts;
9479 
9480         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9481         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9482         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9483       }
9484 
9485       /* Compute c_i, f_i and their norms */
9486       for (i = 0; i < nc; i++) {
9487         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9488         fi[i] = fg->centroid[i] - cg->centroid[i];
9489         Ai[i] = fg->normal[i];
9490         normci += PetscPowReal(ci[i], 2);
9491         normfi += PetscPowReal(fi[i], 2);
9492         normai += PetscPowReal(Ai[i], 2);
9493       }
9494       normci = PetscSqrtReal(normci);
9495       normfi = PetscSqrtReal(normfi);
9496       normai = PetscSqrtReal(normai);
9497 
9498       /* Normalize and compute for each face-cell-normal pair */
9499       for (i = 0; i < nc; i++) {
9500         ci[i] = ci[i] / normci;
9501         fi[i] = fi[i] / normfi;
9502         Ai[i] = Ai[i] / normai;
9503         /* PetscAbs because I don't know if normals are guaranteed to point out */
9504         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9505         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9506       }
9507       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9508       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9509     }
9510     PetscCall(PetscFree(adj));
9511     PetscCall(PetscFree2(cArr, fArr));
9512     /* Defer to cell if they're equal */
9513     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9514     if (OrthQualLabel) {
9515       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9516     }
9517   }
9518   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9519   PetscCall(VecAssemblyBegin(*OrthQual));
9520   PetscCall(VecAssemblyEnd(*OrthQual));
9521   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9522   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9523   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9524   if (OrthQualLabel) {
9525     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9526   }
9527   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9528   PetscCall(PetscViewerDestroy(&vwr));
9529   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9530   PetscFunctionReturn(PETSC_SUCCESS);
9531 }
9532 
9533 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9534  * interpolator construction */
9535 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9536 {
9537   PetscSection section, newSection, gsection;
9538   PetscSF      sf;
9539   PetscBool    hasConstraints, ghasConstraints;
9540 
9541   PetscFunctionBegin;
9542   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9543   PetscAssertPointer(odm, 2);
9544   PetscCall(DMGetLocalSection(dm, &section));
9545   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9546   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9547   if (!ghasConstraints) {
9548     PetscCall(PetscObjectReference((PetscObject)dm));
9549     *odm = dm;
9550     PetscFunctionReturn(PETSC_SUCCESS);
9551   }
9552   PetscCall(DMClone(dm, odm));
9553   PetscCall(DMCopyFields(dm, *odm));
9554   PetscCall(DMGetLocalSection(*odm, &newSection));
9555   PetscCall(DMGetPointSF(*odm, &sf));
9556   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9557   PetscCall(DMSetGlobalSection(*odm, gsection));
9558   PetscCall(PetscSectionDestroy(&gsection));
9559   PetscFunctionReturn(PETSC_SUCCESS);
9560 }
9561 
9562 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9563 {
9564   DM        dmco, dmfo;
9565   Mat       interpo;
9566   Vec       rscale;
9567   Vec       cglobalo, clocal;
9568   Vec       fglobal, fglobalo, flocal;
9569   PetscBool regular;
9570 
9571   PetscFunctionBegin;
9572   PetscCall(DMGetFullDM(dmc, &dmco));
9573   PetscCall(DMGetFullDM(dmf, &dmfo));
9574   PetscCall(DMSetCoarseDM(dmfo, dmco));
9575   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9576   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9577   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9578   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9579   PetscCall(DMCreateLocalVector(dmc, &clocal));
9580   PetscCall(VecSet(cglobalo, 0.));
9581   PetscCall(VecSet(clocal, 0.));
9582   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9583   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9584   PetscCall(DMCreateLocalVector(dmf, &flocal));
9585   PetscCall(VecSet(fglobal, 0.));
9586   PetscCall(VecSet(fglobalo, 0.));
9587   PetscCall(VecSet(flocal, 0.));
9588   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9589   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9590   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9591   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9592   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9593   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9594   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9595   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9596   *shift = fglobal;
9597   PetscCall(VecDestroy(&flocal));
9598   PetscCall(VecDestroy(&fglobalo));
9599   PetscCall(VecDestroy(&clocal));
9600   PetscCall(VecDestroy(&cglobalo));
9601   PetscCall(VecDestroy(&rscale));
9602   PetscCall(MatDestroy(&interpo));
9603   PetscCall(DMDestroy(&dmfo));
9604   PetscCall(DMDestroy(&dmco));
9605   PetscFunctionReturn(PETSC_SUCCESS);
9606 }
9607 
9608 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9609 {
9610   PetscObject shifto;
9611   Vec         shift;
9612 
9613   PetscFunctionBegin;
9614   if (!interp) {
9615     Vec rscale;
9616 
9617     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9618     PetscCall(VecDestroy(&rscale));
9619   } else {
9620     PetscCall(PetscObjectReference((PetscObject)interp));
9621   }
9622   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9623   if (!shifto) {
9624     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9625     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9626     shifto = (PetscObject)shift;
9627     PetscCall(VecDestroy(&shift));
9628   }
9629   shift = (Vec)shifto;
9630   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9631   PetscCall(VecAXPY(fineSol, 1.0, shift));
9632   PetscCall(MatDestroy(&interp));
9633   PetscFunctionReturn(PETSC_SUCCESS);
9634 }
9635 
9636 /* Pointwise interpolation
9637      Just code FEM for now
9638      u^f = I u^c
9639      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9640      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9641      I_{ij} = psi^f_i phi^c_j
9642 */
9643 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9644 {
9645   PetscSection gsc, gsf;
9646   PetscInt     m, n;
9647   void        *ctx;
9648   DM           cdm;
9649   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9650 
9651   PetscFunctionBegin;
9652   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9653   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9654   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9655   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9656 
9657   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9658   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9659   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9660   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9661   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9662 
9663   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9664   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9665   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9666   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9667   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9668   if (scaling) {
9669     /* Use naive scaling */
9670     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9671   }
9672   PetscFunctionReturn(PETSC_SUCCESS);
9673 }
9674 
9675 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9676 {
9677   VecScatter ctx;
9678 
9679   PetscFunctionBegin;
9680   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9681   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9682   PetscCall(VecScatterDestroy(&ctx));
9683   PetscFunctionReturn(PETSC_SUCCESS);
9684 }
9685 
9686 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[])
9687 {
9688   const PetscInt Nc = uOff[1] - uOff[0];
9689   PetscInt       c;
9690   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9691 }
9692 
9693 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9694 {
9695   DM           dmc;
9696   PetscDS      ds;
9697   Vec          ones, locmass;
9698   IS           cellIS;
9699   PetscFormKey key;
9700   PetscInt     depth;
9701 
9702   PetscFunctionBegin;
9703   PetscCall(DMClone(dm, &dmc));
9704   PetscCall(DMCopyDisc(dm, dmc));
9705   PetscCall(DMGetDS(dmc, &ds));
9706   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9707   PetscCall(DMCreateGlobalVector(dmc, mass));
9708   PetscCall(DMGetLocalVector(dmc, &ones));
9709   PetscCall(DMGetLocalVector(dmc, &locmass));
9710   PetscCall(DMPlexGetDepth(dmc, &depth));
9711   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9712   PetscCall(VecSet(locmass, 0.0));
9713   PetscCall(VecSet(ones, 1.0));
9714   key.label = NULL;
9715   key.value = 0;
9716   key.field = 0;
9717   key.part  = 0;
9718   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9719   PetscCall(ISDestroy(&cellIS));
9720   PetscCall(VecSet(*mass, 0.0));
9721   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9722   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9723   PetscCall(DMRestoreLocalVector(dmc, &ones));
9724   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9725   PetscCall(DMDestroy(&dmc));
9726   PetscFunctionReturn(PETSC_SUCCESS);
9727 }
9728 
9729 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9730 {
9731   PetscSection gsc, gsf;
9732   PetscInt     m, n;
9733   void        *ctx;
9734   DM           cdm;
9735   PetscBool    regular;
9736 
9737   PetscFunctionBegin;
9738   if (dmFine == dmCoarse) {
9739     DM            dmc;
9740     PetscDS       ds;
9741     PetscWeakForm wf;
9742     Vec           u;
9743     IS            cellIS;
9744     PetscFormKey  key;
9745     PetscInt      depth;
9746 
9747     PetscCall(DMClone(dmFine, &dmc));
9748     PetscCall(DMCopyDisc(dmFine, dmc));
9749     PetscCall(DMGetDS(dmc, &ds));
9750     PetscCall(PetscDSGetWeakForm(ds, &wf));
9751     PetscCall(PetscWeakFormClear(wf));
9752     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9753     PetscCall(DMCreateMatrix(dmc, mass));
9754     PetscCall(DMGetLocalVector(dmc, &u));
9755     PetscCall(DMPlexGetDepth(dmc, &depth));
9756     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9757     PetscCall(MatZeroEntries(*mass));
9758     key.label = NULL;
9759     key.value = 0;
9760     key.field = 0;
9761     key.part  = 0;
9762     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9763     PetscCall(ISDestroy(&cellIS));
9764     PetscCall(DMRestoreLocalVector(dmc, &u));
9765     PetscCall(DMDestroy(&dmc));
9766   } else {
9767     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9768     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9769     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9770     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9771 
9772     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9773     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9774     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9775     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9776 
9777     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9778     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9779     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9780     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9781   }
9782   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9783   PetscFunctionReturn(PETSC_SUCCESS);
9784 }
9785 
9786 /*@
9787   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9788 
9789   Input Parameter:
9790 . dm - The `DMPLEX` object
9791 
9792   Output Parameter:
9793 . regular - The flag
9794 
9795   Level: intermediate
9796 
9797 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9798 @*/
9799 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9800 {
9801   PetscFunctionBegin;
9802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9803   PetscAssertPointer(regular, 2);
9804   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9805   PetscFunctionReturn(PETSC_SUCCESS);
9806 }
9807 
9808 /*@
9809   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9810 
9811   Input Parameters:
9812 + dm      - The `DMPLEX` object
9813 - regular - The flag
9814 
9815   Level: intermediate
9816 
9817 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9818 @*/
9819 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9820 {
9821   PetscFunctionBegin;
9822   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9823   ((DM_Plex *)dm->data)->regularRefinement = regular;
9824   PetscFunctionReturn(PETSC_SUCCESS);
9825 }
9826 
9827 /*@
9828   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9829   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9830 
9831   Not Collective
9832 
9833   Input Parameter:
9834 . dm - The `DMPLEX` object
9835 
9836   Output Parameters:
9837 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9838 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9839 
9840   Level: intermediate
9841 
9842 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9843 @*/
9844 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9845 {
9846   DM_Plex *plex = (DM_Plex *)dm->data;
9847 
9848   PetscFunctionBegin;
9849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9850   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9851   if (anchorSection) *anchorSection = plex->anchorSection;
9852   if (anchorIS) *anchorIS = plex->anchorIS;
9853   PetscFunctionReturn(PETSC_SUCCESS);
9854 }
9855 
9856 /*@
9857   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
9858 
9859   Collective
9860 
9861   Input Parameters:
9862 + dm            - The `DMPLEX` object
9863 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9864                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9865 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9866 
9867   Level: intermediate
9868 
9869   Notes:
9870   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
9871   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
9872   combination of other points' degrees of freedom.
9873 
9874   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9875   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9876 
9877   The reference counts of `anchorSection` and `anchorIS` are incremented.
9878 
9879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9880 @*/
9881 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9882 {
9883   DM_Plex    *plex = (DM_Plex *)dm->data;
9884   PetscMPIInt result;
9885 
9886   PetscFunctionBegin;
9887   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9888   if (anchorSection) {
9889     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9890     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9891     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9892   }
9893   if (anchorIS) {
9894     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9895     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9896     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9897   }
9898 
9899   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9900   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9901   plex->anchorSection = anchorSection;
9902 
9903   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9904   PetscCall(ISDestroy(&plex->anchorIS));
9905   plex->anchorIS = anchorIS;
9906 
9907   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9908     PetscInt        size, a, pStart, pEnd;
9909     const PetscInt *anchors;
9910 
9911     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9912     PetscCall(ISGetLocalSize(anchorIS, &size));
9913     PetscCall(ISGetIndices(anchorIS, &anchors));
9914     for (a = 0; a < size; a++) {
9915       PetscInt p;
9916 
9917       p = anchors[a];
9918       if (p >= pStart && p < pEnd) {
9919         PetscInt dof;
9920 
9921         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9922         if (dof) {
9923           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9924           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9925         }
9926       }
9927     }
9928     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9929   }
9930   /* reset the generic constraints */
9931   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9932   PetscFunctionReturn(PETSC_SUCCESS);
9933 }
9934 
9935 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9936 {
9937   PetscSection anchorSection;
9938   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9939 
9940   PetscFunctionBegin;
9941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9942   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9943   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9944   PetscCall(PetscSectionGetNumFields(section, &numFields));
9945   if (numFields) {
9946     PetscInt f;
9947     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9948 
9949     for (f = 0; f < numFields; f++) {
9950       PetscInt numComp;
9951 
9952       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9953       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9954     }
9955   }
9956   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9957   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9958   pStart = PetscMax(pStart, sStart);
9959   pEnd   = PetscMin(pEnd, sEnd);
9960   pEnd   = PetscMax(pStart, pEnd);
9961   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9962   for (p = pStart; p < pEnd; p++) {
9963     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9964     if (dof) {
9965       PetscCall(PetscSectionGetDof(section, p, &dof));
9966       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9967       for (f = 0; f < numFields; f++) {
9968         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9969         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9970       }
9971     }
9972   }
9973   PetscCall(PetscSectionSetUp(*cSec));
9974   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9975   PetscFunctionReturn(PETSC_SUCCESS);
9976 }
9977 
9978 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9979 {
9980   PetscSection    aSec;
9981   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9982   const PetscInt *anchors;
9983   PetscInt        numFields, f;
9984   IS              aIS;
9985   MatType         mtype;
9986   PetscBool       iscuda, iskokkos;
9987 
9988   PetscFunctionBegin;
9989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9990   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9991   PetscCall(PetscSectionGetStorageSize(section, &n));
9992   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9993   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9994   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9995   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9996   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9997   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9998   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9999   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10000   else mtype = MATSEQAIJ;
10001   PetscCall(MatSetType(*cMat, mtype));
10002   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10003   PetscCall(ISGetIndices(aIS, &anchors));
10004   /* cSec will be a subset of aSec and section */
10005   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10006   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10007   PetscCall(PetscMalloc1(m + 1, &i));
10008   i[0] = 0;
10009   PetscCall(PetscSectionGetNumFields(section, &numFields));
10010   for (p = pStart; p < pEnd; p++) {
10011     PetscInt rDof, rOff, r;
10012 
10013     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10014     if (!rDof) continue;
10015     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10016     if (numFields) {
10017       for (f = 0; f < numFields; f++) {
10018         annz = 0;
10019         for (r = 0; r < rDof; r++) {
10020           a = anchors[rOff + r];
10021           if (a < sStart || a >= sEnd) continue;
10022           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10023           annz += aDof;
10024         }
10025         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10026         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10027         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10028       }
10029     } else {
10030       annz = 0;
10031       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10032       for (q = 0; q < dof; q++) {
10033         a = anchors[rOff + q];
10034         if (a < sStart || a >= sEnd) continue;
10035         PetscCall(PetscSectionGetDof(section, a, &aDof));
10036         annz += aDof;
10037       }
10038       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10039       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10040       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10041     }
10042   }
10043   nnz = i[m];
10044   PetscCall(PetscMalloc1(nnz, &j));
10045   offset = 0;
10046   for (p = pStart; p < pEnd; p++) {
10047     if (numFields) {
10048       for (f = 0; f < numFields; f++) {
10049         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10050         for (q = 0; q < dof; q++) {
10051           PetscInt rDof, rOff, r;
10052           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10053           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10054           for (r = 0; r < rDof; r++) {
10055             PetscInt s;
10056 
10057             a = anchors[rOff + r];
10058             if (a < sStart || a >= sEnd) continue;
10059             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10060             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10061             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10062           }
10063         }
10064       }
10065     } else {
10066       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10067       for (q = 0; q < dof; q++) {
10068         PetscInt rDof, rOff, r;
10069         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10070         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10071         for (r = 0; r < rDof; r++) {
10072           PetscInt s;
10073 
10074           a = anchors[rOff + r];
10075           if (a < sStart || a >= sEnd) continue;
10076           PetscCall(PetscSectionGetDof(section, a, &aDof));
10077           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10078           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10079         }
10080       }
10081     }
10082   }
10083   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10084   PetscCall(PetscFree(i));
10085   PetscCall(PetscFree(j));
10086   PetscCall(ISRestoreIndices(aIS, &anchors));
10087   PetscFunctionReturn(PETSC_SUCCESS);
10088 }
10089 
10090 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10091 {
10092   DM_Plex     *plex = (DM_Plex *)dm->data;
10093   PetscSection anchorSection, section, cSec;
10094   Mat          cMat;
10095 
10096   PetscFunctionBegin;
10097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10098   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10099   if (anchorSection) {
10100     PetscInt Nf;
10101 
10102     PetscCall(DMGetLocalSection(dm, &section));
10103     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10104     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10105     PetscCall(DMGetNumFields(dm, &Nf));
10106     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10107     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10108     PetscCall(PetscSectionDestroy(&cSec));
10109     PetscCall(MatDestroy(&cMat));
10110   }
10111   PetscFunctionReturn(PETSC_SUCCESS);
10112 }
10113 
10114 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10115 {
10116   IS           subis;
10117   PetscSection section, subsection;
10118 
10119   PetscFunctionBegin;
10120   PetscCall(DMGetLocalSection(dm, &section));
10121   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10122   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10123   /* Create subdomain */
10124   PetscCall(DMPlexFilter(dm, label, value, subdm));
10125   /* Create submodel */
10126   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10127   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10128   PetscCall(DMSetLocalSection(*subdm, subsection));
10129   PetscCall(PetscSectionDestroy(&subsection));
10130   PetscCall(DMCopyDisc(dm, *subdm));
10131   /* Create map from submodel to global model */
10132   if (is) {
10133     PetscSection    sectionGlobal, subsectionGlobal;
10134     IS              spIS;
10135     const PetscInt *spmap;
10136     PetscInt       *subIndices;
10137     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10138     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10139 
10140     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10141     PetscCall(ISGetIndices(spIS, &spmap));
10142     PetscCall(PetscSectionGetNumFields(section, &Nf));
10143     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10144     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10145     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10146     for (p = pStart; p < pEnd; ++p) {
10147       PetscInt gdof, pSubSize = 0;
10148 
10149       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10150       if (gdof > 0) {
10151         for (f = 0; f < Nf; ++f) {
10152           PetscInt fdof, fcdof;
10153 
10154           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10155           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10156           pSubSize += fdof - fcdof;
10157         }
10158         subSize += pSubSize;
10159         if (pSubSize) {
10160           if (bs < 0) {
10161             bs = pSubSize;
10162           } else if (bs != pSubSize) {
10163             /* Layout does not admit a pointwise block size */
10164             bs = 1;
10165           }
10166         }
10167       }
10168     }
10169     /* Must have same blocksize on all procs (some might have no points) */
10170     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10171     bsLocal[1] = bs;
10172     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10173     if (bsMinMax[0] != bsMinMax[1]) {
10174       bs = 1;
10175     } else {
10176       bs = bsMinMax[0];
10177     }
10178     PetscCall(PetscMalloc1(subSize, &subIndices));
10179     for (p = pStart; p < pEnd; ++p) {
10180       PetscInt gdof, goff;
10181 
10182       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10183       if (gdof > 0) {
10184         const PetscInt point = spmap[p];
10185 
10186         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10187         for (f = 0; f < Nf; ++f) {
10188           PetscInt fdof, fcdof, fc, f2, poff = 0;
10189 
10190           /* Can get rid of this loop by storing field information in the global section */
10191           for (f2 = 0; f2 < f; ++f2) {
10192             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10193             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10194             poff += fdof - fcdof;
10195           }
10196           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10197           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10198           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10199         }
10200       }
10201     }
10202     PetscCall(ISRestoreIndices(spIS, &spmap));
10203     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10204     if (bs > 1) {
10205       /* We need to check that the block size does not come from non-contiguous fields */
10206       PetscInt i, j, set = 1;
10207       for (i = 0; i < subSize; i += bs) {
10208         for (j = 0; j < bs; ++j) {
10209           if (subIndices[i + j] != subIndices[i] + j) {
10210             set = 0;
10211             break;
10212           }
10213         }
10214       }
10215       if (set) PetscCall(ISSetBlockSize(*is, bs));
10216     }
10217     /* Attach nullspace */
10218     for (f = 0; f < Nf; ++f) {
10219       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10220       if ((*subdm)->nullspaceConstructors[f]) break;
10221     }
10222     if (f < Nf) {
10223       MatNullSpace nullSpace;
10224       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10225 
10226       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10227       PetscCall(MatNullSpaceDestroy(&nullSpace));
10228     }
10229   }
10230   PetscFunctionReturn(PETSC_SUCCESS);
10231 }
10232 
10233 /*@
10234   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10235 
10236   Input Parameters:
10237 + dm    - The `DM`
10238 - dummy - unused argument
10239 
10240   Options Database Key:
10241 . -dm_plex_monitor_throughput - Activate the monitor
10242 
10243   Level: developer
10244 
10245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10246 @*/
10247 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10248 {
10249   PetscLogHandler default_handler;
10250 
10251   PetscFunctionBegin;
10252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10253   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10254   if (default_handler) {
10255     PetscLogEvent      event;
10256     PetscEventPerfInfo eventInfo;
10257     PetscReal          cellRate, flopRate;
10258     PetscInt           cStart, cEnd, Nf, N;
10259     const char        *name;
10260 
10261     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10262     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10263     PetscCall(DMGetNumFields(dm, &Nf));
10264     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10265     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10266     N        = (cEnd - cStart) * Nf * eventInfo.count;
10267     flopRate = eventInfo.flops / eventInfo.time;
10268     cellRate = N / eventInfo.time;
10269     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)));
10270   } else {
10271     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.");
10272   }
10273   PetscFunctionReturn(PETSC_SUCCESS);
10274 }
10275