xref: /petsc/src/dm/impls/plex/plex.c (revision 66af8762ec03dbef0e079729eb2a1734a35ed7ff)
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 /*
5457  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5458  representing a line in the section.
5459 */
5460 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5461 {
5462   PetscFunctionBeginHot;
5463   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5464   if (line < 0) {
5465     *k  = 0;
5466     *Nc = 0;
5467   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5468     *k = 1;
5469   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5470     /* An order k SEM disc has k-1 dofs on an edge */
5471     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5472     *k = *k / *Nc + 1;
5473   }
5474   PetscFunctionReturn(PETSC_SUCCESS);
5475 }
5476 
5477 /*@
5478 
5479   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5480   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5481   section provided (or the section of the `DM`).
5482 
5483   Input Parameters:
5484 + dm      - The `DM`
5485 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5486 - section - The `PetscSection` to reorder, or `NULL` for the default section
5487 
5488   Example:
5489   A typical interpolated single-quad mesh might order points as
5490 .vb
5491   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5492 
5493   v4 -- e6 -- v3
5494   |           |
5495   e7    c0    e8
5496   |           |
5497   v1 -- e5 -- v2
5498 .ve
5499 
5500   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5501   dofs in the order of points, e.g.,
5502 .vb
5503     c0 -> [0,1,2,3]
5504     v1 -> [4]
5505     ...
5506     e5 -> [8, 9]
5507 .ve
5508 
5509   which corresponds to the dofs
5510 .vb
5511     6   10  11  7
5512     13  2   3   15
5513     12  0   1   14
5514     4   8   9   5
5515 .ve
5516 
5517   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5518 .vb
5519   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5520 .ve
5521 
5522   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5523 .vb
5524    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5525 .ve
5526 
5527   Level: developer
5528 
5529   Notes:
5530   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5531   degree of the basis.
5532 
5533   This is required to run with libCEED.
5534 
5535 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5536 @*/
5537 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5538 {
5539   DMLabel   label;
5540   PetscInt  dim, depth = -1, eStart = -1, Nf;
5541   PetscBool vertexchart;
5542 
5543   PetscFunctionBegin;
5544   PetscCall(DMGetDimension(dm, &dim));
5545   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5546   if (point < 0) {
5547     PetscInt sStart, sEnd;
5548 
5549     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5550     point = sEnd - sStart ? sStart : point;
5551   }
5552   PetscCall(DMPlexGetDepthLabel(dm, &label));
5553   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5554   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5555   if (depth == 1) {
5556     eStart = point;
5557   } else if (depth == dim) {
5558     const PetscInt *cone;
5559 
5560     PetscCall(DMPlexGetCone(dm, point, &cone));
5561     if (dim == 2) eStart = cone[0];
5562     else if (dim == 3) {
5563       const PetscInt *cone2;
5564       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5565       eStart = cone2[0];
5566     } 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);
5567   } 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);
5568   { /* Determine whether the chart covers all points or just vertices. */
5569     PetscInt pStart, pEnd, cStart, cEnd;
5570     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5571     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5572     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5573     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5574     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5575   }
5576   PetscCall(PetscSectionGetNumFields(section, &Nf));
5577   for (PetscInt d = 1; d <= dim; d++) {
5578     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5579     PetscInt *perm;
5580 
5581     for (f = 0; f < Nf; ++f) {
5582       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5583       size += PetscPowInt(k + 1, d) * Nc;
5584     }
5585     PetscCall(PetscMalloc1(size, &perm));
5586     for (f = 0; f < Nf; ++f) {
5587       switch (d) {
5588       case 1:
5589         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5590         /*
5591          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5592          We want              [ vtx0; edge of length k-1; vtx1 ]
5593          */
5594         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5595         for (i = 0; i < k - 1; i++)
5596           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5597         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5598         foffset = offset;
5599         break;
5600       case 2:
5601         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5602         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5603         /* The SEM order is
5604 
5605          v_lb, {e_b}, v_rb,
5606          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5607          v_lt, reverse {e_t}, v_rt
5608          */
5609         {
5610           const PetscInt of   = 0;
5611           const PetscInt oeb  = of + PetscSqr(k - 1);
5612           const PetscInt oer  = oeb + (k - 1);
5613           const PetscInt oet  = oer + (k - 1);
5614           const PetscInt oel  = oet + (k - 1);
5615           const PetscInt ovlb = oel + (k - 1);
5616           const PetscInt ovrb = ovlb + 1;
5617           const PetscInt ovrt = ovrb + 1;
5618           const PetscInt ovlt = ovrt + 1;
5619           PetscInt       o;
5620 
5621           /* bottom */
5622           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5623           for (o = oeb; o < oer; ++o)
5624             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5625           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5626           /* middle */
5627           for (i = 0; i < k - 1; ++i) {
5628             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5629             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5630               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5631             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5632           }
5633           /* top */
5634           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5635           for (o = oel - 1; o >= oet; --o)
5636             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5637           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5638           foffset = offset;
5639         }
5640         break;
5641       case 3:
5642         /* The original hex closure is
5643 
5644          {c,
5645          f_b, f_t, f_f, f_b, f_r, f_l,
5646          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5647          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5648          */
5649         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5650         /* The SEM order is
5651          Bottom Slice
5652          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5653          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5654          v_blb, {e_bb}, v_brb,
5655 
5656          Middle Slice (j)
5657          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5658          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5659          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5660 
5661          Top Slice
5662          v_tlf, {e_tf}, v_trf,
5663          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5664          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5665          */
5666         {
5667           const PetscInt oc    = 0;
5668           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5669           const PetscInt oft   = ofb + PetscSqr(k - 1);
5670           const PetscInt off   = oft + PetscSqr(k - 1);
5671           const PetscInt ofk   = off + PetscSqr(k - 1);
5672           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5673           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5674           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5675           const PetscInt oebb  = oebl + (k - 1);
5676           const PetscInt oebr  = oebb + (k - 1);
5677           const PetscInt oebf  = oebr + (k - 1);
5678           const PetscInt oetf  = oebf + (k - 1);
5679           const PetscInt oetr  = oetf + (k - 1);
5680           const PetscInt oetb  = oetr + (k - 1);
5681           const PetscInt oetl  = oetb + (k - 1);
5682           const PetscInt oerf  = oetl + (k - 1);
5683           const PetscInt oelf  = oerf + (k - 1);
5684           const PetscInt oelb  = oelf + (k - 1);
5685           const PetscInt oerb  = oelb + (k - 1);
5686           const PetscInt ovblf = oerb + (k - 1);
5687           const PetscInt ovblb = ovblf + 1;
5688           const PetscInt ovbrb = ovblb + 1;
5689           const PetscInt ovbrf = ovbrb + 1;
5690           const PetscInt ovtlf = ovbrf + 1;
5691           const PetscInt ovtrf = ovtlf + 1;
5692           const PetscInt ovtrb = ovtrf + 1;
5693           const PetscInt ovtlb = ovtrb + 1;
5694           PetscInt       o, n;
5695 
5696           /* Bottom Slice */
5697           /*   bottom */
5698           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5699           for (o = oetf - 1; o >= oebf; --o)
5700             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5701           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5702           /*   middle */
5703           for (i = 0; i < k - 1; ++i) {
5704             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5705             for (n = 0; n < k - 1; ++n) {
5706               o = ofb + n * (k - 1) + i;
5707               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5708             }
5709             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5710           }
5711           /*   top */
5712           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5713           for (o = oebb; o < oebr; ++o)
5714             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5715           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5716 
5717           /* Middle Slice */
5718           for (j = 0; j < k - 1; ++j) {
5719             /*   bottom */
5720             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5721             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5722               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5723             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5724             /*   middle */
5725             for (i = 0; i < k - 1; ++i) {
5726               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5727               for (n = 0; n < k - 1; ++n)
5728                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5729               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5730             }
5731             /*   top */
5732             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5733             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5734               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5735             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5736           }
5737 
5738           /* Top Slice */
5739           /*   bottom */
5740           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5741           for (o = oetf; o < oetr; ++o)
5742             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5743           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5744           /*   middle */
5745           for (i = 0; i < k - 1; ++i) {
5746             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5747             for (n = 0; n < k - 1; ++n)
5748               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5749             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5750           }
5751           /*   top */
5752           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5753           for (o = oetl - 1; o >= oetb; --o)
5754             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5755           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5756 
5757           foffset = offset;
5758         }
5759         break;
5760       default:
5761         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5762       }
5763     }
5764     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5765     /* Check permutation */
5766     {
5767       PetscInt *check;
5768 
5769       PetscCall(PetscMalloc1(size, &check));
5770       for (i = 0; i < size; ++i) {
5771         check[i] = -1;
5772         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5773       }
5774       for (i = 0; i < size; ++i) check[perm[i]] = i;
5775       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5776       PetscCall(PetscFree(check));
5777     }
5778     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5779     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5780       PetscInt *loc_perm;
5781       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5782       for (PetscInt i = 0; i < size; i++) {
5783         loc_perm[i]        = perm[i];
5784         loc_perm[size + i] = size + perm[i];
5785       }
5786       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5787     }
5788   }
5789   PetscFunctionReturn(PETSC_SUCCESS);
5790 }
5791 
5792 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5793 {
5794   PetscDS  prob;
5795   PetscInt depth, Nf, h;
5796   DMLabel  label;
5797 
5798   PetscFunctionBeginHot;
5799   PetscCall(DMGetDS(dm, &prob));
5800   Nf      = prob->Nf;
5801   label   = dm->depthLabel;
5802   *dspace = NULL;
5803   if (field < Nf) {
5804     PetscObject disc = prob->disc[field];
5805 
5806     if (disc->classid == PETSCFE_CLASSID) {
5807       PetscDualSpace dsp;
5808 
5809       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5810       PetscCall(DMLabelGetNumValues(label, &depth));
5811       PetscCall(DMLabelGetValue(label, point, &h));
5812       h = depth - 1 - h;
5813       if (h) {
5814         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5815       } else {
5816         *dspace = dsp;
5817       }
5818     }
5819   }
5820   PetscFunctionReturn(PETSC_SUCCESS);
5821 }
5822 
5823 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5824 {
5825   PetscScalar       *array;
5826   const PetscScalar *vArray;
5827   const PetscInt    *cone, *coneO;
5828   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5829 
5830   PetscFunctionBeginHot;
5831   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5832   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5833   PetscCall(DMPlexGetCone(dm, point, &cone));
5834   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5835   if (!values || !*values) {
5836     if ((point >= pStart) && (point < pEnd)) {
5837       PetscInt dof;
5838 
5839       PetscCall(PetscSectionGetDof(section, point, &dof));
5840       size += dof;
5841     }
5842     for (p = 0; p < numPoints; ++p) {
5843       const PetscInt cp = cone[p];
5844       PetscInt       dof;
5845 
5846       if ((cp < pStart) || (cp >= pEnd)) continue;
5847       PetscCall(PetscSectionGetDof(section, cp, &dof));
5848       size += dof;
5849     }
5850     if (!values) {
5851       if (csize) *csize = size;
5852       PetscFunctionReturn(PETSC_SUCCESS);
5853     }
5854     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5855   } else {
5856     array = *values;
5857   }
5858   size = 0;
5859   PetscCall(VecGetArrayRead(v, &vArray));
5860   if ((point >= pStart) && (point < pEnd)) {
5861     PetscInt           dof, off, d;
5862     const PetscScalar *varr;
5863 
5864     PetscCall(PetscSectionGetDof(section, point, &dof));
5865     PetscCall(PetscSectionGetOffset(section, point, &off));
5866     varr = &vArray[off];
5867     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5868     size += dof;
5869   }
5870   for (p = 0; p < numPoints; ++p) {
5871     const PetscInt     cp = cone[p];
5872     PetscInt           o  = coneO[p];
5873     PetscInt           dof, off, d;
5874     const PetscScalar *varr;
5875 
5876     if ((cp < pStart) || (cp >= pEnd)) continue;
5877     PetscCall(PetscSectionGetDof(section, cp, &dof));
5878     PetscCall(PetscSectionGetOffset(section, cp, &off));
5879     varr = &vArray[off];
5880     if (o >= 0) {
5881       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5882     } else {
5883       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5884     }
5885     size += dof;
5886   }
5887   PetscCall(VecRestoreArrayRead(v, &vArray));
5888   if (!*values) {
5889     if (csize) *csize = size;
5890     *values = array;
5891   } else {
5892     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5893     *csize = size;
5894   }
5895   PetscFunctionReturn(PETSC_SUCCESS);
5896 }
5897 
5898 /* Compress out points not in the section */
5899 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5900 {
5901   const PetscInt np = *numPoints;
5902   PetscInt       pStart, pEnd, p, q;
5903 
5904   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5905   for (p = 0, q = 0; p < np; ++p) {
5906     const PetscInt r = points[p * 2];
5907     if ((r >= pStart) && (r < pEnd)) {
5908       points[q * 2]     = r;
5909       points[q * 2 + 1] = points[p * 2 + 1];
5910       ++q;
5911     }
5912   }
5913   *numPoints = q;
5914   return PETSC_SUCCESS;
5915 }
5916 
5917 /* Compressed closure does not apply closure permutation */
5918 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5919 {
5920   const PetscInt *cla = NULL;
5921   PetscInt        np, *pts = NULL;
5922 
5923   PetscFunctionBeginHot;
5924   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5925   if (!ornt && *clPoints) {
5926     PetscInt dof, off;
5927 
5928     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5929     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5930     PetscCall(ISGetIndices(*clPoints, &cla));
5931     np  = dof / 2;
5932     pts = (PetscInt *)&cla[off];
5933   } else {
5934     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5935     PetscCall(CompressPoints_Private(section, &np, pts));
5936   }
5937   *numPoints = np;
5938   *points    = pts;
5939   *clp       = cla;
5940   PetscFunctionReturn(PETSC_SUCCESS);
5941 }
5942 
5943 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5944 {
5945   PetscFunctionBeginHot;
5946   if (!*clPoints) {
5947     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5948   } else {
5949     PetscCall(ISRestoreIndices(*clPoints, clp));
5950   }
5951   *numPoints = 0;
5952   *points    = NULL;
5953   *clSec     = NULL;
5954   *clPoints  = NULL;
5955   *clp       = NULL;
5956   PetscFunctionReturn(PETSC_SUCCESS);
5957 }
5958 
5959 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5960 {
5961   PetscInt            offset = 0, p;
5962   const PetscInt    **perms  = NULL;
5963   const PetscScalar **flips  = NULL;
5964 
5965   PetscFunctionBeginHot;
5966   *size = 0;
5967   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5968   for (p = 0; p < numPoints; p++) {
5969     const PetscInt     point = points[2 * p];
5970     const PetscInt    *perm  = perms ? perms[p] : NULL;
5971     const PetscScalar *flip  = flips ? flips[p] : NULL;
5972     PetscInt           dof, off, d;
5973     const PetscScalar *varr;
5974 
5975     PetscCall(PetscSectionGetDof(section, point, &dof));
5976     PetscCall(PetscSectionGetOffset(section, point, &off));
5977     varr = &vArray[off];
5978     if (clperm) {
5979       if (perm) {
5980         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5981       } else {
5982         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5983       }
5984       if (flip) {
5985         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5986       }
5987     } else {
5988       if (perm) {
5989         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5990       } else {
5991         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5992       }
5993       if (flip) {
5994         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5995       }
5996     }
5997     offset += dof;
5998   }
5999   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6000   *size = offset;
6001   PetscFunctionReturn(PETSC_SUCCESS);
6002 }
6003 
6004 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[])
6005 {
6006   PetscInt offset = 0, f;
6007 
6008   PetscFunctionBeginHot;
6009   *size = 0;
6010   for (f = 0; f < numFields; ++f) {
6011     PetscInt            p;
6012     const PetscInt    **perms = NULL;
6013     const PetscScalar **flips = NULL;
6014 
6015     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6016     for (p = 0; p < numPoints; p++) {
6017       const PetscInt     point = points[2 * p];
6018       PetscInt           fdof, foff, b;
6019       const PetscScalar *varr;
6020       const PetscInt    *perm = perms ? perms[p] : NULL;
6021       const PetscScalar *flip = flips ? flips[p] : NULL;
6022 
6023       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6024       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6025       varr = &vArray[foff];
6026       if (clperm) {
6027         if (perm) {
6028           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6029         } else {
6030           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6031         }
6032         if (flip) {
6033           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6034         }
6035       } else {
6036         if (perm) {
6037           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6038         } else {
6039           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6040         }
6041         if (flip) {
6042           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6043         }
6044       }
6045       offset += fdof;
6046     }
6047     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6048   }
6049   *size = offset;
6050   PetscFunctionReturn(PETSC_SUCCESS);
6051 }
6052 
6053 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6054 {
6055   PetscSection    clSection;
6056   IS              clPoints;
6057   PetscInt       *points = NULL;
6058   const PetscInt *clp, *perm = NULL;
6059   PetscInt        depth, numFields, numPoints, asize;
6060 
6061   PetscFunctionBeginHot;
6062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6063   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6064   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6065   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6066   PetscCall(DMPlexGetDepth(dm, &depth));
6067   PetscCall(PetscSectionGetNumFields(section, &numFields));
6068   if (depth == 1 && numFields < 2) {
6069     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6070     PetscFunctionReturn(PETSC_SUCCESS);
6071   }
6072   /* Get points */
6073   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6074   /* Get sizes */
6075   asize = 0;
6076   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6077     PetscInt dof;
6078     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6079     asize += dof;
6080   }
6081   if (values) {
6082     const PetscScalar *vArray;
6083     PetscInt           size;
6084 
6085     if (*values) {
6086       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);
6087     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6088     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6089     PetscCall(VecGetArrayRead(v, &vArray));
6090     /* Get values */
6091     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6092     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6093     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6094     /* Cleanup array */
6095     PetscCall(VecRestoreArrayRead(v, &vArray));
6096   }
6097   if (csize) *csize = asize;
6098   /* Cleanup points */
6099   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6100   PetscFunctionReturn(PETSC_SUCCESS);
6101 }
6102 
6103 /*@C
6104   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6105 
6106   Not collective
6107 
6108   Input Parameters:
6109 + dm      - The `DM`
6110 . section - The section describing the layout in `v`, or `NULL` to use the default section
6111 . v       - The local vector
6112 - point   - The point in the `DM`
6113 
6114   Input/Output Parameters:
6115 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6116 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6117            if the user provided `NULL`, it is a borrowed array and should not be freed
6118 
6119   Level: intermediate
6120 
6121   Notes:
6122   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6123   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6124   assembly function, and a user may already have allocated storage for this operation.
6125 
6126   A typical use could be
6127 .vb
6128    values = NULL;
6129    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6130    for (cl = 0; cl < clSize; ++cl) {
6131      <Compute on closure>
6132    }
6133    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6134 .ve
6135   or
6136 .vb
6137    PetscMalloc1(clMaxSize, &values);
6138    for (p = pStart; p < pEnd; ++p) {
6139      clSize = clMaxSize;
6140      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6141      for (cl = 0; cl < clSize; ++cl) {
6142        <Compute on closure>
6143      }
6144    }
6145    PetscFree(values);
6146 .ve
6147 
6148   Fortran Notes:
6149   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6150 
6151 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6152 @*/
6153 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6154 {
6155   PetscFunctionBeginHot;
6156   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6157   PetscFunctionReturn(PETSC_SUCCESS);
6158 }
6159 
6160 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6161 {
6162   DMLabel            depthLabel;
6163   PetscSection       clSection;
6164   IS                 clPoints;
6165   PetscScalar       *array;
6166   const PetscScalar *vArray;
6167   PetscInt          *points = NULL;
6168   const PetscInt    *clp, *perm = NULL;
6169   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6170 
6171   PetscFunctionBeginHot;
6172   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6173   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6174   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6175   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6176   PetscCall(DMPlexGetDepth(dm, &mdepth));
6177   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6178   PetscCall(PetscSectionGetNumFields(section, &numFields));
6179   if (mdepth == 1 && numFields < 2) {
6180     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6181     PetscFunctionReturn(PETSC_SUCCESS);
6182   }
6183   /* Get points */
6184   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6185   for (clsize = 0, p = 0; p < Np; p++) {
6186     PetscInt dof;
6187     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6188     clsize += dof;
6189   }
6190   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6191   /* Filter points */
6192   for (p = 0; p < numPoints * 2; p += 2) {
6193     PetscInt dep;
6194 
6195     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6196     if (dep != depth) continue;
6197     points[Np * 2 + 0] = points[p];
6198     points[Np * 2 + 1] = points[p + 1];
6199     ++Np;
6200   }
6201   /* Get array */
6202   if (!values || !*values) {
6203     PetscInt asize = 0, dof;
6204 
6205     for (p = 0; p < Np * 2; p += 2) {
6206       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6207       asize += dof;
6208     }
6209     if (!values) {
6210       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6211       if (csize) *csize = asize;
6212       PetscFunctionReturn(PETSC_SUCCESS);
6213     }
6214     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6215   } else {
6216     array = *values;
6217   }
6218   PetscCall(VecGetArrayRead(v, &vArray));
6219   /* Get values */
6220   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6221   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6222   /* Cleanup points */
6223   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6224   /* Cleanup array */
6225   PetscCall(VecRestoreArrayRead(v, &vArray));
6226   if (!*values) {
6227     if (csize) *csize = size;
6228     *values = array;
6229   } else {
6230     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6231     *csize = size;
6232   }
6233   PetscFunctionReturn(PETSC_SUCCESS);
6234 }
6235 
6236 /*@C
6237   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6238 
6239   Not collective
6240 
6241   Input Parameters:
6242 + dm      - The `DM`
6243 . section - The section describing the layout in `v`, or `NULL` to use the default section
6244 . v       - The local vector
6245 . point   - The point in the `DM`
6246 . csize   - The number of values in the closure, or `NULL`
6247 - values  - The array of values, which is a borrowed array and should not be freed
6248 
6249   Level: intermediate
6250 
6251   Note:
6252   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6253 
6254   Fortran Notes:
6255   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6256 
6257 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6258 @*/
6259 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6260 {
6261   PetscInt size = 0;
6262 
6263   PetscFunctionBegin;
6264   /* Should work without recalculating size */
6265   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6266   *values = NULL;
6267   PetscFunctionReturn(PETSC_SUCCESS);
6268 }
6269 
6270 static inline void add(PetscScalar *x, PetscScalar y)
6271 {
6272   *x += y;
6273 }
6274 static inline void insert(PetscScalar *x, PetscScalar y)
6275 {
6276   *x = y;
6277 }
6278 
6279 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[])
6280 {
6281   PetscInt        cdof;  /* The number of constraints on this point */
6282   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6283   PetscScalar    *a;
6284   PetscInt        off, cind = 0, k;
6285 
6286   PetscFunctionBegin;
6287   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6288   PetscCall(PetscSectionGetOffset(section, point, &off));
6289   a = &array[off];
6290   if (!cdof || setBC) {
6291     if (clperm) {
6292       if (perm) {
6293         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6294       } else {
6295         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6296       }
6297     } else {
6298       if (perm) {
6299         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6300       } else {
6301         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6302       }
6303     }
6304   } else {
6305     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6306     if (clperm) {
6307       if (perm) {
6308         for (k = 0; k < dof; ++k) {
6309           if ((cind < cdof) && (k == cdofs[cind])) {
6310             ++cind;
6311             continue;
6312           }
6313           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6314         }
6315       } else {
6316         for (k = 0; k < dof; ++k) {
6317           if ((cind < cdof) && (k == cdofs[cind])) {
6318             ++cind;
6319             continue;
6320           }
6321           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6322         }
6323       }
6324     } else {
6325       if (perm) {
6326         for (k = 0; k < dof; ++k) {
6327           if ((cind < cdof) && (k == cdofs[cind])) {
6328             ++cind;
6329             continue;
6330           }
6331           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6332         }
6333       } else {
6334         for (k = 0; k < dof; ++k) {
6335           if ((cind < cdof) && (k == cdofs[cind])) {
6336             ++cind;
6337             continue;
6338           }
6339           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6340         }
6341       }
6342     }
6343   }
6344   PetscFunctionReturn(PETSC_SUCCESS);
6345 }
6346 
6347 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[])
6348 {
6349   PetscInt        cdof;  /* The number of constraints on this point */
6350   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6351   PetscScalar    *a;
6352   PetscInt        off, cind = 0, k;
6353 
6354   PetscFunctionBegin;
6355   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6356   PetscCall(PetscSectionGetOffset(section, point, &off));
6357   a = &array[off];
6358   if (cdof) {
6359     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6360     if (clperm) {
6361       if (perm) {
6362         for (k = 0; k < dof; ++k) {
6363           if ((cind < cdof) && (k == cdofs[cind])) {
6364             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6365             cind++;
6366           }
6367         }
6368       } else {
6369         for (k = 0; k < dof; ++k) {
6370           if ((cind < cdof) && (k == cdofs[cind])) {
6371             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6372             cind++;
6373           }
6374         }
6375       }
6376     } else {
6377       if (perm) {
6378         for (k = 0; k < dof; ++k) {
6379           if ((cind < cdof) && (k == cdofs[cind])) {
6380             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6381             cind++;
6382           }
6383         }
6384       } else {
6385         for (k = 0; k < dof; ++k) {
6386           if ((cind < cdof) && (k == cdofs[cind])) {
6387             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6388             cind++;
6389           }
6390         }
6391       }
6392     }
6393   }
6394   PetscFunctionReturn(PETSC_SUCCESS);
6395 }
6396 
6397 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[])
6398 {
6399   PetscScalar    *a;
6400   PetscInt        fdof, foff, fcdof, foffset = *offset;
6401   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6402   PetscInt        cind = 0, b;
6403 
6404   PetscFunctionBegin;
6405   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6406   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6407   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6408   a = &array[foff];
6409   if (!fcdof || setBC) {
6410     if (clperm) {
6411       if (perm) {
6412         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6413       } else {
6414         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6415       }
6416     } else {
6417       if (perm) {
6418         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6419       } else {
6420         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6421       }
6422     }
6423   } else {
6424     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6425     if (clperm) {
6426       if (perm) {
6427         for (b = 0; b < fdof; b++) {
6428           if ((cind < fcdof) && (b == fcdofs[cind])) {
6429             ++cind;
6430             continue;
6431           }
6432           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6433         }
6434       } else {
6435         for (b = 0; b < fdof; b++) {
6436           if ((cind < fcdof) && (b == fcdofs[cind])) {
6437             ++cind;
6438             continue;
6439           }
6440           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6441         }
6442       }
6443     } else {
6444       if (perm) {
6445         for (b = 0; b < fdof; b++) {
6446           if ((cind < fcdof) && (b == fcdofs[cind])) {
6447             ++cind;
6448             continue;
6449           }
6450           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6451         }
6452       } else {
6453         for (b = 0; b < fdof; b++) {
6454           if ((cind < fcdof) && (b == fcdofs[cind])) {
6455             ++cind;
6456             continue;
6457           }
6458           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6459         }
6460       }
6461     }
6462   }
6463   *offset += fdof;
6464   PetscFunctionReturn(PETSC_SUCCESS);
6465 }
6466 
6467 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[])
6468 {
6469   PetscScalar    *a;
6470   PetscInt        fdof, foff, fcdof, foffset = *offset;
6471   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6472   PetscInt        Nc, cind = 0, ncind = 0, b;
6473   PetscBool       ncSet, fcSet;
6474 
6475   PetscFunctionBegin;
6476   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6477   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6478   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6479   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6480   a = &array[foff];
6481   if (fcdof) {
6482     /* We just override fcdof and fcdofs with Ncc and comps */
6483     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6484     if (clperm) {
6485       if (perm) {
6486         if (comps) {
6487           for (b = 0; b < fdof; b++) {
6488             ncSet = fcSet = PETSC_FALSE;
6489             if (b % Nc == comps[ncind]) {
6490               ncind = (ncind + 1) % Ncc;
6491               ncSet = PETSC_TRUE;
6492             }
6493             if ((cind < fcdof) && (b == fcdofs[cind])) {
6494               ++cind;
6495               fcSet = PETSC_TRUE;
6496             }
6497             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6498           }
6499         } else {
6500           for (b = 0; b < fdof; b++) {
6501             if ((cind < fcdof) && (b == fcdofs[cind])) {
6502               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6503               ++cind;
6504             }
6505           }
6506         }
6507       } else {
6508         if (comps) {
6509           for (b = 0; b < fdof; b++) {
6510             ncSet = fcSet = PETSC_FALSE;
6511             if (b % Nc == comps[ncind]) {
6512               ncind = (ncind + 1) % Ncc;
6513               ncSet = PETSC_TRUE;
6514             }
6515             if ((cind < fcdof) && (b == fcdofs[cind])) {
6516               ++cind;
6517               fcSet = PETSC_TRUE;
6518             }
6519             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6520           }
6521         } else {
6522           for (b = 0; b < fdof; b++) {
6523             if ((cind < fcdof) && (b == fcdofs[cind])) {
6524               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6525               ++cind;
6526             }
6527           }
6528         }
6529       }
6530     } else {
6531       if (perm) {
6532         if (comps) {
6533           for (b = 0; b < fdof; b++) {
6534             ncSet = fcSet = PETSC_FALSE;
6535             if (b % Nc == comps[ncind]) {
6536               ncind = (ncind + 1) % Ncc;
6537               ncSet = PETSC_TRUE;
6538             }
6539             if ((cind < fcdof) && (b == fcdofs[cind])) {
6540               ++cind;
6541               fcSet = PETSC_TRUE;
6542             }
6543             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6544           }
6545         } else {
6546           for (b = 0; b < fdof; b++) {
6547             if ((cind < fcdof) && (b == fcdofs[cind])) {
6548               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6549               ++cind;
6550             }
6551           }
6552         }
6553       } else {
6554         if (comps) {
6555           for (b = 0; b < fdof; b++) {
6556             ncSet = fcSet = PETSC_FALSE;
6557             if (b % Nc == comps[ncind]) {
6558               ncind = (ncind + 1) % Ncc;
6559               ncSet = PETSC_TRUE;
6560             }
6561             if ((cind < fcdof) && (b == fcdofs[cind])) {
6562               ++cind;
6563               fcSet = PETSC_TRUE;
6564             }
6565             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6566           }
6567         } else {
6568           for (b = 0; b < fdof; b++) {
6569             if ((cind < fcdof) && (b == fcdofs[cind])) {
6570               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6571               ++cind;
6572             }
6573           }
6574         }
6575       }
6576     }
6577   }
6578   *offset += fdof;
6579   PetscFunctionReturn(PETSC_SUCCESS);
6580 }
6581 
6582 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6583 {
6584   PetscScalar    *array;
6585   const PetscInt *cone, *coneO;
6586   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6587 
6588   PetscFunctionBeginHot;
6589   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6590   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6591   PetscCall(DMPlexGetCone(dm, point, &cone));
6592   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6593   PetscCall(VecGetArray(v, &array));
6594   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6595     const PetscInt cp = !p ? point : cone[p - 1];
6596     const PetscInt o  = !p ? 0 : coneO[p - 1];
6597 
6598     if ((cp < pStart) || (cp >= pEnd)) {
6599       dof = 0;
6600       continue;
6601     }
6602     PetscCall(PetscSectionGetDof(section, cp, &dof));
6603     /* ADD_VALUES */
6604     {
6605       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6606       PetscScalar    *a;
6607       PetscInt        cdof, coff, cind = 0, k;
6608 
6609       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6610       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6611       a = &array[coff];
6612       if (!cdof) {
6613         if (o >= 0) {
6614           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6615         } else {
6616           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6617         }
6618       } else {
6619         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6620         if (o >= 0) {
6621           for (k = 0; k < dof; ++k) {
6622             if ((cind < cdof) && (k == cdofs[cind])) {
6623               ++cind;
6624               continue;
6625             }
6626             a[k] += values[off + k];
6627           }
6628         } else {
6629           for (k = 0; k < dof; ++k) {
6630             if ((cind < cdof) && (k == cdofs[cind])) {
6631               ++cind;
6632               continue;
6633             }
6634             a[k] += values[off + dof - k - 1];
6635           }
6636         }
6637       }
6638     }
6639   }
6640   PetscCall(VecRestoreArray(v, &array));
6641   PetscFunctionReturn(PETSC_SUCCESS);
6642 }
6643 
6644 /*@C
6645   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6646 
6647   Not collective
6648 
6649   Input Parameters:
6650 + dm      - The `DM`
6651 . section - The section describing the layout in `v`, or `NULL` to use the default section
6652 . v       - The local vector
6653 . point   - The point in the `DM`
6654 . values  - The array of values
6655 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6656          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6657 
6658   Level: intermediate
6659 
6660 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6661 @*/
6662 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6663 {
6664   PetscSection    clSection;
6665   IS              clPoints;
6666   PetscScalar    *array;
6667   PetscInt       *points = NULL;
6668   const PetscInt *clp, *clperm = NULL;
6669   PetscInt        depth, numFields, numPoints, p, clsize;
6670 
6671   PetscFunctionBeginHot;
6672   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6673   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6674   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6675   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6676   PetscCall(DMPlexGetDepth(dm, &depth));
6677   PetscCall(PetscSectionGetNumFields(section, &numFields));
6678   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6679     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6680     PetscFunctionReturn(PETSC_SUCCESS);
6681   }
6682   /* Get points */
6683   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6684   for (clsize = 0, p = 0; p < numPoints; p++) {
6685     PetscInt dof;
6686     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6687     clsize += dof;
6688   }
6689   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6690   /* Get array */
6691   PetscCall(VecGetArray(v, &array));
6692   /* Get values */
6693   if (numFields > 0) {
6694     PetscInt offset = 0, f;
6695     for (f = 0; f < numFields; ++f) {
6696       const PetscInt    **perms = NULL;
6697       const PetscScalar **flips = NULL;
6698 
6699       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6700       switch (mode) {
6701       case INSERT_VALUES:
6702         for (p = 0; p < numPoints; p++) {
6703           const PetscInt     point = points[2 * p];
6704           const PetscInt    *perm  = perms ? perms[p] : NULL;
6705           const PetscScalar *flip  = flips ? flips[p] : NULL;
6706           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6707         }
6708         break;
6709       case INSERT_ALL_VALUES:
6710         for (p = 0; p < numPoints; p++) {
6711           const PetscInt     point = points[2 * p];
6712           const PetscInt    *perm  = perms ? perms[p] : NULL;
6713           const PetscScalar *flip  = flips ? flips[p] : NULL;
6714           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6715         }
6716         break;
6717       case INSERT_BC_VALUES:
6718         for (p = 0; p < numPoints; p++) {
6719           const PetscInt     point = points[2 * p];
6720           const PetscInt    *perm  = perms ? perms[p] : NULL;
6721           const PetscScalar *flip  = flips ? flips[p] : NULL;
6722           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6723         }
6724         break;
6725       case ADD_VALUES:
6726         for (p = 0; p < numPoints; p++) {
6727           const PetscInt     point = points[2 * p];
6728           const PetscInt    *perm  = perms ? perms[p] : NULL;
6729           const PetscScalar *flip  = flips ? flips[p] : NULL;
6730           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6731         }
6732         break;
6733       case ADD_ALL_VALUES:
6734         for (p = 0; p < numPoints; p++) {
6735           const PetscInt     point = points[2 * p];
6736           const PetscInt    *perm  = perms ? perms[p] : NULL;
6737           const PetscScalar *flip  = flips ? flips[p] : NULL;
6738           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6739         }
6740         break;
6741       case ADD_BC_VALUES:
6742         for (p = 0; p < numPoints; p++) {
6743           const PetscInt     point = points[2 * p];
6744           const PetscInt    *perm  = perms ? perms[p] : NULL;
6745           const PetscScalar *flip  = flips ? flips[p] : NULL;
6746           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6747         }
6748         break;
6749       default:
6750         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6751       }
6752       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6753     }
6754   } else {
6755     PetscInt            dof, off;
6756     const PetscInt    **perms = NULL;
6757     const PetscScalar **flips = NULL;
6758 
6759     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6760     switch (mode) {
6761     case INSERT_VALUES:
6762       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6763         const PetscInt     point = points[2 * p];
6764         const PetscInt    *perm  = perms ? perms[p] : NULL;
6765         const PetscScalar *flip  = flips ? flips[p] : NULL;
6766         PetscCall(PetscSectionGetDof(section, point, &dof));
6767         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6768       }
6769       break;
6770     case INSERT_ALL_VALUES:
6771       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6772         const PetscInt     point = points[2 * p];
6773         const PetscInt    *perm  = perms ? perms[p] : NULL;
6774         const PetscScalar *flip  = flips ? flips[p] : NULL;
6775         PetscCall(PetscSectionGetDof(section, point, &dof));
6776         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6777       }
6778       break;
6779     case INSERT_BC_VALUES:
6780       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6781         const PetscInt     point = points[2 * p];
6782         const PetscInt    *perm  = perms ? perms[p] : NULL;
6783         const PetscScalar *flip  = flips ? flips[p] : NULL;
6784         PetscCall(PetscSectionGetDof(section, point, &dof));
6785         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6786       }
6787       break;
6788     case ADD_VALUES:
6789       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6790         const PetscInt     point = points[2 * p];
6791         const PetscInt    *perm  = perms ? perms[p] : NULL;
6792         const PetscScalar *flip  = flips ? flips[p] : NULL;
6793         PetscCall(PetscSectionGetDof(section, point, &dof));
6794         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6795       }
6796       break;
6797     case ADD_ALL_VALUES:
6798       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6799         const PetscInt     point = points[2 * p];
6800         const PetscInt    *perm  = perms ? perms[p] : NULL;
6801         const PetscScalar *flip  = flips ? flips[p] : NULL;
6802         PetscCall(PetscSectionGetDof(section, point, &dof));
6803         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6804       }
6805       break;
6806     case ADD_BC_VALUES:
6807       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6808         const PetscInt     point = points[2 * p];
6809         const PetscInt    *perm  = perms ? perms[p] : NULL;
6810         const PetscScalar *flip  = flips ? flips[p] : NULL;
6811         PetscCall(PetscSectionGetDof(section, point, &dof));
6812         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6813       }
6814       break;
6815     default:
6816       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6817     }
6818     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6819   }
6820   /* Cleanup points */
6821   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6822   /* Cleanup array */
6823   PetscCall(VecRestoreArray(v, &array));
6824   PetscFunctionReturn(PETSC_SUCCESS);
6825 }
6826 
6827 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6828 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6829 {
6830   PetscFunctionBegin;
6831   *contains = PETSC_TRUE;
6832   if (label) {
6833     PetscInt fdof;
6834 
6835     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6836     if (!*contains) {
6837       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6838       *offset += fdof;
6839       PetscFunctionReturn(PETSC_SUCCESS);
6840     }
6841   }
6842   PetscFunctionReturn(PETSC_SUCCESS);
6843 }
6844 
6845 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6846 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)
6847 {
6848   PetscSection    clSection;
6849   IS              clPoints;
6850   PetscScalar    *array;
6851   PetscInt       *points = NULL;
6852   const PetscInt *clp;
6853   PetscInt        numFields, numPoints, p;
6854   PetscInt        offset = 0, f;
6855 
6856   PetscFunctionBeginHot;
6857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6858   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6859   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6860   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6861   PetscCall(PetscSectionGetNumFields(section, &numFields));
6862   /* Get points */
6863   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6864   /* Get array */
6865   PetscCall(VecGetArray(v, &array));
6866   /* Get values */
6867   for (f = 0; f < numFields; ++f) {
6868     const PetscInt    **perms = NULL;
6869     const PetscScalar **flips = NULL;
6870     PetscBool           contains;
6871 
6872     if (!fieldActive[f]) {
6873       for (p = 0; p < numPoints * 2; p += 2) {
6874         PetscInt fdof;
6875         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6876         offset += fdof;
6877       }
6878       continue;
6879     }
6880     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6881     switch (mode) {
6882     case INSERT_VALUES:
6883       for (p = 0; p < numPoints; p++) {
6884         const PetscInt     point = points[2 * p];
6885         const PetscInt    *perm  = perms ? perms[p] : NULL;
6886         const PetscScalar *flip  = flips ? flips[p] : NULL;
6887         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6888         if (!contains) continue;
6889         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6890       }
6891       break;
6892     case INSERT_ALL_VALUES:
6893       for (p = 0; p < numPoints; p++) {
6894         const PetscInt     point = points[2 * p];
6895         const PetscInt    *perm  = perms ? perms[p] : NULL;
6896         const PetscScalar *flip  = flips ? flips[p] : NULL;
6897         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6898         if (!contains) continue;
6899         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6900       }
6901       break;
6902     case INSERT_BC_VALUES:
6903       for (p = 0; p < numPoints; p++) {
6904         const PetscInt     point = points[2 * p];
6905         const PetscInt    *perm  = perms ? perms[p] : NULL;
6906         const PetscScalar *flip  = flips ? flips[p] : NULL;
6907         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6908         if (!contains) continue;
6909         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6910       }
6911       break;
6912     case ADD_VALUES:
6913       for (p = 0; p < numPoints; p++) {
6914         const PetscInt     point = points[2 * p];
6915         const PetscInt    *perm  = perms ? perms[p] : NULL;
6916         const PetscScalar *flip  = flips ? flips[p] : NULL;
6917         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6918         if (!contains) continue;
6919         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6920       }
6921       break;
6922     case ADD_ALL_VALUES:
6923       for (p = 0; p < numPoints; p++) {
6924         const PetscInt     point = points[2 * p];
6925         const PetscInt    *perm  = perms ? perms[p] : NULL;
6926         const PetscScalar *flip  = flips ? flips[p] : NULL;
6927         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6928         if (!contains) continue;
6929         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6930       }
6931       break;
6932     default:
6933       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6934     }
6935     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6936   }
6937   /* Cleanup points */
6938   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6939   /* Cleanup array */
6940   PetscCall(VecRestoreArray(v, &array));
6941   PetscFunctionReturn(PETSC_SUCCESS);
6942 }
6943 
6944 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6945 {
6946   PetscMPIInt rank;
6947   PetscInt    i, j;
6948 
6949   PetscFunctionBegin;
6950   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6951   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6952   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6953   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6954   numCIndices = numCIndices ? numCIndices : numRIndices;
6955   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6956   for (i = 0; i < numRIndices; i++) {
6957     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6958     for (j = 0; j < numCIndices; j++) {
6959 #if defined(PETSC_USE_COMPLEX)
6960       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6961 #else
6962       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6963 #endif
6964     }
6965     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6966   }
6967   PetscFunctionReturn(PETSC_SUCCESS);
6968 }
6969 
6970 /*
6971   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6972 
6973   Input Parameters:
6974 + section - The section for this data layout
6975 . islocal - Is the section (and thus indices being requested) local or global?
6976 . point   - The point contributing dofs with these indices
6977 . off     - The global offset of this point
6978 . loff    - The local offset of each field
6979 . setBC   - The flag determining whether to include indices of boundary values
6980 . perm    - A permutation of the dofs on this point, or NULL
6981 - indperm - A permutation of the entire indices array, or NULL
6982 
6983   Output Parameter:
6984 . indices - Indices for dofs on this point
6985 
6986   Level: developer
6987 
6988   Note: The indices could be local or global, depending on the value of 'off'.
6989 */
6990 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6991 {
6992   PetscInt        dof;   /* The number of unknowns on this point */
6993   PetscInt        cdof;  /* The number of constraints on this point */
6994   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6995   PetscInt        cind = 0, k;
6996 
6997   PetscFunctionBegin;
6998   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6999   PetscCall(PetscSectionGetDof(section, point, &dof));
7000   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7001   if (!cdof || setBC) {
7002     for (k = 0; k < dof; ++k) {
7003       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7004       const PetscInt ind    = indperm ? indperm[preind] : preind;
7005 
7006       indices[ind] = off + k;
7007     }
7008   } else {
7009     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7010     for (k = 0; k < dof; ++k) {
7011       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7012       const PetscInt ind    = indperm ? indperm[preind] : preind;
7013 
7014       if ((cind < cdof) && (k == cdofs[cind])) {
7015         /* Insert check for returning constrained indices */
7016         indices[ind] = -(off + k + 1);
7017         ++cind;
7018       } else {
7019         indices[ind] = off + k - (islocal ? 0 : cind);
7020       }
7021     }
7022   }
7023   *loff += dof;
7024   PetscFunctionReturn(PETSC_SUCCESS);
7025 }
7026 
7027 /*
7028  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7029 
7030  Input Parameters:
7031 + section - a section (global or local)
7032 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7033 . point - point within section
7034 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7035 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7036 . setBC - identify constrained (boundary condition) points via involution.
7037 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7038 . permsoff - offset
7039 - indperm - index permutation
7040 
7041  Output Parameter:
7042 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7043 . indices - array to hold indices (as defined by section) of each dof associated with point
7044 
7045  Notes:
7046  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7047  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7048  in the local vector.
7049 
7050  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7051  significant).  It is invalid to call with a global section and setBC=true.
7052 
7053  Developer Note:
7054  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7055  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7056  offset could be obtained from the section instead of passing it explicitly as we do now.
7057 
7058  Example:
7059  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7060  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7061  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7062  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.
7063 
7064  Level: developer
7065 */
7066 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[])
7067 {
7068   PetscInt numFields, foff, f;
7069 
7070   PetscFunctionBegin;
7071   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7072   PetscCall(PetscSectionGetNumFields(section, &numFields));
7073   for (f = 0, foff = 0; f < numFields; ++f) {
7074     PetscInt        fdof, cfdof;
7075     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7076     PetscInt        cind = 0, b;
7077     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7078 
7079     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7080     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7081     if (!cfdof || setBC) {
7082       for (b = 0; b < fdof; ++b) {
7083         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7084         const PetscInt ind    = indperm ? indperm[preind] : preind;
7085 
7086         indices[ind] = off + foff + b;
7087       }
7088     } else {
7089       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7090       for (b = 0; b < fdof; ++b) {
7091         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7092         const PetscInt ind    = indperm ? indperm[preind] : preind;
7093 
7094         if ((cind < cfdof) && (b == fcdofs[cind])) {
7095           indices[ind] = -(off + foff + b + 1);
7096           ++cind;
7097         } else {
7098           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7099         }
7100       }
7101     }
7102     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7103     foffs[f] += fdof;
7104   }
7105   PetscFunctionReturn(PETSC_SUCCESS);
7106 }
7107 
7108 /*
7109   This version believes the globalSection offsets for each field, rather than just the point offset
7110 
7111  . foffs - The offset into 'indices' for each field, since it is segregated by field
7112 
7113  Notes:
7114  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7115  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7116 */
7117 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7118 {
7119   PetscInt numFields, foff, f;
7120 
7121   PetscFunctionBegin;
7122   PetscCall(PetscSectionGetNumFields(section, &numFields));
7123   for (f = 0; f < numFields; ++f) {
7124     PetscInt        fdof, cfdof;
7125     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7126     PetscInt        cind = 0, b;
7127     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7128 
7129     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7130     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7131     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7132     if (!cfdof) {
7133       for (b = 0; b < fdof; ++b) {
7134         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7135         const PetscInt ind    = indperm ? indperm[preind] : preind;
7136 
7137         indices[ind] = foff + b;
7138       }
7139     } else {
7140       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7141       for (b = 0; b < fdof; ++b) {
7142         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7143         const PetscInt ind    = indperm ? indperm[preind] : preind;
7144 
7145         if ((cind < cfdof) && (b == fcdofs[cind])) {
7146           indices[ind] = -(foff + b + 1);
7147           ++cind;
7148         } else {
7149           indices[ind] = foff + b - cind;
7150         }
7151       }
7152     }
7153     foffs[f] += fdof;
7154   }
7155   PetscFunctionReturn(PETSC_SUCCESS);
7156 }
7157 
7158 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)
7159 {
7160   Mat             cMat;
7161   PetscSection    aSec, cSec;
7162   IS              aIS;
7163   PetscInt        aStart = -1, aEnd = -1;
7164   const PetscInt *anchors;
7165   PetscInt        numFields, f, p, q, newP = 0;
7166   PetscInt        newNumPoints = 0, newNumIndices = 0;
7167   PetscInt       *newPoints, *indices, *newIndices;
7168   PetscInt        maxAnchor, maxDof;
7169   PetscInt        newOffsets[32];
7170   PetscInt       *pointMatOffsets[32];
7171   PetscInt       *newPointOffsets[32];
7172   PetscScalar    *pointMat[32];
7173   PetscScalar    *newValues      = NULL, *tmpValues;
7174   PetscBool       anyConstrained = PETSC_FALSE;
7175 
7176   PetscFunctionBegin;
7177   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7178   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7179   PetscCall(PetscSectionGetNumFields(section, &numFields));
7180 
7181   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7182   /* if there are point-to-point constraints */
7183   if (aSec) {
7184     PetscCall(PetscArrayzero(newOffsets, 32));
7185     PetscCall(ISGetIndices(aIS, &anchors));
7186     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7187     /* figure out how many points are going to be in the new element matrix
7188      * (we allow double counting, because it's all just going to be summed
7189      * into the global matrix anyway) */
7190     for (p = 0; p < 2 * numPoints; p += 2) {
7191       PetscInt b    = points[p];
7192       PetscInt bDof = 0, bSecDof;
7193 
7194       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7195       if (!bSecDof) continue;
7196       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7197       if (bDof) {
7198         /* this point is constrained */
7199         /* it is going to be replaced by its anchors */
7200         PetscInt bOff, q;
7201 
7202         anyConstrained = PETSC_TRUE;
7203         newNumPoints += bDof;
7204         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7205         for (q = 0; q < bDof; q++) {
7206           PetscInt a = anchors[bOff + q];
7207           PetscInt aDof;
7208 
7209           PetscCall(PetscSectionGetDof(section, a, &aDof));
7210           newNumIndices += aDof;
7211           for (f = 0; f < numFields; ++f) {
7212             PetscInt fDof;
7213 
7214             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7215             newOffsets[f + 1] += fDof;
7216           }
7217         }
7218       } else {
7219         /* this point is not constrained */
7220         newNumPoints++;
7221         newNumIndices += bSecDof;
7222         for (f = 0; f < numFields; ++f) {
7223           PetscInt fDof;
7224 
7225           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7226           newOffsets[f + 1] += fDof;
7227         }
7228       }
7229     }
7230   }
7231   if (!anyConstrained) {
7232     if (outNumPoints) *outNumPoints = 0;
7233     if (outNumIndices) *outNumIndices = 0;
7234     if (outPoints) *outPoints = NULL;
7235     if (outValues) *outValues = NULL;
7236     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7237     PetscFunctionReturn(PETSC_SUCCESS);
7238   }
7239 
7240   if (outNumPoints) *outNumPoints = newNumPoints;
7241   if (outNumIndices) *outNumIndices = newNumIndices;
7242 
7243   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7244 
7245   if (!outPoints && !outValues) {
7246     if (offsets) {
7247       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7248     }
7249     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7250     PetscFunctionReturn(PETSC_SUCCESS);
7251   }
7252 
7253   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7254 
7255   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7256 
7257   /* workspaces */
7258   if (numFields) {
7259     for (f = 0; f < numFields; f++) {
7260       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7261       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7262     }
7263   } else {
7264     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7265     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7266   }
7267 
7268   /* get workspaces for the point-to-point matrices */
7269   if (numFields) {
7270     PetscInt totalOffset, totalMatOffset;
7271 
7272     for (p = 0; p < numPoints; p++) {
7273       PetscInt b    = points[2 * p];
7274       PetscInt bDof = 0, bSecDof;
7275 
7276       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7277       if (!bSecDof) {
7278         for (f = 0; f < numFields; f++) {
7279           newPointOffsets[f][p + 1] = 0;
7280           pointMatOffsets[f][p + 1] = 0;
7281         }
7282         continue;
7283       }
7284       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7285       if (bDof) {
7286         for (f = 0; f < numFields; f++) {
7287           PetscInt fDof, q, bOff, allFDof = 0;
7288 
7289           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7290           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7291           for (q = 0; q < bDof; q++) {
7292             PetscInt a = anchors[bOff + q];
7293             PetscInt aFDof;
7294 
7295             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7296             allFDof += aFDof;
7297           }
7298           newPointOffsets[f][p + 1] = allFDof;
7299           pointMatOffsets[f][p + 1] = fDof * allFDof;
7300         }
7301       } else {
7302         for (f = 0; f < numFields; f++) {
7303           PetscInt fDof;
7304 
7305           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7306           newPointOffsets[f][p + 1] = fDof;
7307           pointMatOffsets[f][p + 1] = 0;
7308         }
7309       }
7310     }
7311     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7312       newPointOffsets[f][0] = totalOffset;
7313       pointMatOffsets[f][0] = totalMatOffset;
7314       for (p = 0; p < numPoints; p++) {
7315         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7316         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7317       }
7318       totalOffset    = newPointOffsets[f][numPoints];
7319       totalMatOffset = pointMatOffsets[f][numPoints];
7320       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7321     }
7322   } else {
7323     for (p = 0; p < numPoints; p++) {
7324       PetscInt b    = points[2 * p];
7325       PetscInt bDof = 0, bSecDof;
7326 
7327       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7328       if (!bSecDof) {
7329         newPointOffsets[0][p + 1] = 0;
7330         pointMatOffsets[0][p + 1] = 0;
7331         continue;
7332       }
7333       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7334       if (bDof) {
7335         PetscInt bOff, q, allDof = 0;
7336 
7337         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7338         for (q = 0; q < bDof; q++) {
7339           PetscInt a = anchors[bOff + q], aDof;
7340 
7341           PetscCall(PetscSectionGetDof(section, a, &aDof));
7342           allDof += aDof;
7343         }
7344         newPointOffsets[0][p + 1] = allDof;
7345         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7346       } else {
7347         newPointOffsets[0][p + 1] = bSecDof;
7348         pointMatOffsets[0][p + 1] = 0;
7349       }
7350     }
7351     newPointOffsets[0][0] = 0;
7352     pointMatOffsets[0][0] = 0;
7353     for (p = 0; p < numPoints; p++) {
7354       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7355       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7356     }
7357     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7358   }
7359 
7360   /* output arrays */
7361   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7362 
7363   /* get the point-to-point matrices; construct newPoints */
7364   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7365   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7366   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7367   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7368   if (numFields) {
7369     for (p = 0, newP = 0; p < numPoints; p++) {
7370       PetscInt b    = points[2 * p];
7371       PetscInt o    = points[2 * p + 1];
7372       PetscInt bDof = 0, bSecDof;
7373 
7374       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7375       if (!bSecDof) continue;
7376       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7377       if (bDof) {
7378         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7379 
7380         fStart[0] = 0;
7381         fEnd[0]   = 0;
7382         for (f = 0; f < numFields; f++) {
7383           PetscInt fDof;
7384 
7385           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7386           fStart[f + 1] = fStart[f] + fDof;
7387           fEnd[f + 1]   = fStart[f + 1];
7388         }
7389         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7390         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7391 
7392         fAnchorStart[0] = 0;
7393         fAnchorEnd[0]   = 0;
7394         for (f = 0; f < numFields; f++) {
7395           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7396 
7397           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7398           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7399         }
7400         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7401         for (q = 0; q < bDof; q++) {
7402           PetscInt a = anchors[bOff + q], aOff;
7403 
7404           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7405           newPoints[2 * (newP + q)]     = a;
7406           newPoints[2 * (newP + q) + 1] = 0;
7407           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7408           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7409         }
7410         newP += bDof;
7411 
7412         if (outValues) {
7413           /* get the point-to-point submatrix */
7414           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]));
7415         }
7416       } else {
7417         newPoints[2 * newP]     = b;
7418         newPoints[2 * newP + 1] = o;
7419         newP++;
7420       }
7421     }
7422   } else {
7423     for (p = 0; p < numPoints; p++) {
7424       PetscInt b    = points[2 * p];
7425       PetscInt o    = points[2 * p + 1];
7426       PetscInt bDof = 0, bSecDof;
7427 
7428       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7429       if (!bSecDof) continue;
7430       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7431       if (bDof) {
7432         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7433 
7434         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7435         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7436 
7437         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7438         for (q = 0; q < bDof; q++) {
7439           PetscInt a = anchors[bOff + q], aOff;
7440 
7441           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7442 
7443           newPoints[2 * (newP + q)]     = a;
7444           newPoints[2 * (newP + q) + 1] = 0;
7445           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7446           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7447         }
7448         newP += bDof;
7449 
7450         /* get the point-to-point submatrix */
7451         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7452       } else {
7453         newPoints[2 * newP]     = b;
7454         newPoints[2 * newP + 1] = o;
7455         newP++;
7456       }
7457     }
7458   }
7459 
7460   if (outValues) {
7461     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7462     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7463     /* multiply constraints on the right */
7464     if (numFields) {
7465       for (f = 0; f < numFields; f++) {
7466         PetscInt oldOff = offsets[f];
7467 
7468         for (p = 0; p < numPoints; p++) {
7469           PetscInt cStart = newPointOffsets[f][p];
7470           PetscInt b      = points[2 * p];
7471           PetscInt c, r, k;
7472           PetscInt dof;
7473 
7474           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7475           if (!dof) continue;
7476           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7477             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7478             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7479 
7480             for (r = 0; r < numIndices; r++) {
7481               for (c = 0; c < nCols; c++) {
7482                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7483               }
7484             }
7485           } else {
7486             /* copy this column as is */
7487             for (r = 0; r < numIndices; r++) {
7488               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7489             }
7490           }
7491           oldOff += dof;
7492         }
7493       }
7494     } else {
7495       PetscInt oldOff = 0;
7496       for (p = 0; p < numPoints; p++) {
7497         PetscInt cStart = newPointOffsets[0][p];
7498         PetscInt b      = points[2 * p];
7499         PetscInt c, r, k;
7500         PetscInt dof;
7501 
7502         PetscCall(PetscSectionGetDof(section, b, &dof));
7503         if (!dof) continue;
7504         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7505           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7506           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7507 
7508           for (r = 0; r < numIndices; r++) {
7509             for (c = 0; c < nCols; c++) {
7510               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7511             }
7512           }
7513         } else {
7514           /* copy this column as is */
7515           for (r = 0; r < numIndices; r++) {
7516             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7517           }
7518         }
7519         oldOff += dof;
7520       }
7521     }
7522 
7523     if (multiplyLeft) {
7524       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7525       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7526       /* multiply constraints transpose on the left */
7527       if (numFields) {
7528         for (f = 0; f < numFields; f++) {
7529           PetscInt oldOff = offsets[f];
7530 
7531           for (p = 0; p < numPoints; p++) {
7532             PetscInt rStart = newPointOffsets[f][p];
7533             PetscInt b      = points[2 * p];
7534             PetscInt c, r, k;
7535             PetscInt dof;
7536 
7537             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7538             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7539               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7540               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7541 
7542               for (r = 0; r < nRows; r++) {
7543                 for (c = 0; c < newNumIndices; c++) {
7544                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7545                 }
7546               }
7547             } else {
7548               /* copy this row as is */
7549               for (r = 0; r < dof; r++) {
7550                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7551               }
7552             }
7553             oldOff += dof;
7554           }
7555         }
7556       } else {
7557         PetscInt oldOff = 0;
7558 
7559         for (p = 0; p < numPoints; p++) {
7560           PetscInt rStart = newPointOffsets[0][p];
7561           PetscInt b      = points[2 * p];
7562           PetscInt c, r, k;
7563           PetscInt dof;
7564 
7565           PetscCall(PetscSectionGetDof(section, b, &dof));
7566           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7567             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7568             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7569 
7570             for (r = 0; r < nRows; r++) {
7571               for (c = 0; c < newNumIndices; c++) {
7572                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7573               }
7574             }
7575           } else {
7576             /* copy this row as is */
7577             for (r = 0; r < dof; r++) {
7578               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7579             }
7580           }
7581           oldOff += dof;
7582         }
7583       }
7584 
7585       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7586     } else {
7587       newValues = tmpValues;
7588     }
7589   }
7590 
7591   /* clean up */
7592   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7593   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7594 
7595   if (numFields) {
7596     for (f = 0; f < numFields; f++) {
7597       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7598       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7599       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7600     }
7601   } else {
7602     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7603     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7604     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7605   }
7606   PetscCall(ISRestoreIndices(aIS, &anchors));
7607 
7608   /* output */
7609   if (outPoints) {
7610     *outPoints = newPoints;
7611   } else {
7612     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7613   }
7614   if (outValues) *outValues = newValues;
7615   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7616   PetscFunctionReturn(PETSC_SUCCESS);
7617 }
7618 
7619 /*@C
7620   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7621 
7622   Not collective
7623 
7624   Input Parameters:
7625 + dm         - The `DM`
7626 . section    - The `PetscSection` describing the points (a local section)
7627 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7628 . point      - The point defining the closure
7629 - useClPerm  - Use the closure point permutation if available
7630 
7631   Output Parameters:
7632 + numIndices - The number of dof indices in the closure of point with the input sections
7633 . indices    - The dof indices
7634 . outOffsets - Array to write the field offsets into, or `NULL`
7635 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7636 
7637   Level: advanced
7638 
7639   Notes:
7640   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7641 
7642   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7643   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7644   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7645   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7646   indices (with the above semantics) are implied.
7647 
7648 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7649           `PetscSection`, `DMGetGlobalSection()`
7650 @*/
7651 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7652 {
7653   /* Closure ordering */
7654   PetscSection    clSection;
7655   IS              clPoints;
7656   const PetscInt *clp;
7657   PetscInt       *points;
7658   const PetscInt *clperm = NULL;
7659   /* Dof permutation and sign flips */
7660   const PetscInt    **perms[32] = {NULL};
7661   const PetscScalar **flips[32] = {NULL};
7662   PetscScalar        *valCopy   = NULL;
7663   /* Hanging node constraints */
7664   PetscInt    *pointsC = NULL;
7665   PetscScalar *valuesC = NULL;
7666   PetscInt     NclC, NiC;
7667 
7668   PetscInt *idx;
7669   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7670   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7671 
7672   PetscFunctionBeginHot;
7673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7674   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7675   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7676   if (numIndices) PetscAssertPointer(numIndices, 6);
7677   if (indices) PetscAssertPointer(indices, 7);
7678   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7679   if (values) PetscAssertPointer(values, 9);
7680   PetscCall(PetscSectionGetNumFields(section, &Nf));
7681   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7682   PetscCall(PetscArrayzero(offsets, 32));
7683   /* 1) Get points in closure */
7684   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7685   if (useClPerm) {
7686     PetscInt depth, clsize;
7687     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7688     for (clsize = 0, p = 0; p < Ncl; p++) {
7689       PetscInt dof;
7690       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7691       clsize += dof;
7692     }
7693     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7694   }
7695   /* 2) Get number of indices on these points and field offsets from section */
7696   for (p = 0; p < Ncl * 2; p += 2) {
7697     PetscInt dof, fdof;
7698 
7699     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7700     for (f = 0; f < Nf; ++f) {
7701       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7702       offsets[f + 1] += fdof;
7703     }
7704     Ni += dof;
7705   }
7706   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7707   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7708   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7709   for (f = 0; f < PetscMax(1, Nf); ++f) {
7710     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7711     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7712     /* may need to apply sign changes to the element matrix */
7713     if (values && flips[f]) {
7714       PetscInt foffset = offsets[f];
7715 
7716       for (p = 0; p < Ncl; ++p) {
7717         PetscInt           pnt  = points[2 * p], fdof;
7718         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7719 
7720         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7721         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7722         if (flip) {
7723           PetscInt i, j, k;
7724 
7725           if (!valCopy) {
7726             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7727             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7728             *values = valCopy;
7729           }
7730           for (i = 0; i < fdof; ++i) {
7731             PetscScalar fval = flip[i];
7732 
7733             for (k = 0; k < Ni; ++k) {
7734               valCopy[Ni * (foffset + i) + k] *= fval;
7735               valCopy[Ni * k + (foffset + i)] *= fval;
7736             }
7737           }
7738         }
7739         foffset += fdof;
7740       }
7741     }
7742   }
7743   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7744   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7745   if (NclC) {
7746     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7747     for (f = 0; f < PetscMax(1, Nf); ++f) {
7748       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7749       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7750     }
7751     for (f = 0; f < PetscMax(1, Nf); ++f) {
7752       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7753       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7754     }
7755     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7756     Ncl    = NclC;
7757     Ni     = NiC;
7758     points = pointsC;
7759     if (values) *values = valuesC;
7760   }
7761   /* 5) Calculate indices */
7762   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7763   if (Nf) {
7764     PetscInt  idxOff;
7765     PetscBool useFieldOffsets;
7766 
7767     if (outOffsets) {
7768       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7769     }
7770     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7771     if (useFieldOffsets) {
7772       for (p = 0; p < Ncl; ++p) {
7773         const PetscInt pnt = points[p * 2];
7774 
7775         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7776       }
7777     } else {
7778       for (p = 0; p < Ncl; ++p) {
7779         const PetscInt pnt = points[p * 2];
7780 
7781         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7782         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7783          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7784          * global section. */
7785         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7786       }
7787     }
7788   } else {
7789     PetscInt off = 0, idxOff;
7790 
7791     for (p = 0; p < Ncl; ++p) {
7792       const PetscInt  pnt  = points[p * 2];
7793       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7794 
7795       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7796       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7797        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7798       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7799     }
7800   }
7801   /* 6) Cleanup */
7802   for (f = 0; f < PetscMax(1, Nf); ++f) {
7803     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7804     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7805   }
7806   if (NclC) {
7807     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7808   } else {
7809     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7810   }
7811 
7812   if (numIndices) *numIndices = Ni;
7813   if (indices) *indices = idx;
7814   PetscFunctionReturn(PETSC_SUCCESS);
7815 }
7816 
7817 /*@C
7818   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7819 
7820   Not collective
7821 
7822   Input Parameters:
7823 + dm         - The `DM`
7824 . section    - The `PetscSection` describing the points (a local section)
7825 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7826 . point      - The point defining the closure
7827 - useClPerm  - Use the closure point permutation if available
7828 
7829   Output Parameters:
7830 + numIndices - The number of dof indices in the closure of point with the input sections
7831 . indices    - The dof indices
7832 . outOffsets - Array to write the field offsets into, or `NULL`
7833 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7834 
7835   Level: advanced
7836 
7837   Notes:
7838   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7839 
7840   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7841   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7842   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7843   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7844   indices (with the above semantics) are implied.
7845 
7846 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7847 @*/
7848 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7849 {
7850   PetscFunctionBegin;
7851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7852   PetscAssertPointer(indices, 7);
7853   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7854   PetscFunctionReturn(PETSC_SUCCESS);
7855 }
7856 
7857 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7858 {
7859   DM_Plex           *mesh = (DM_Plex *)dm->data;
7860   PetscInt          *indices;
7861   PetscInt           numIndices;
7862   const PetscScalar *valuesOrig = values;
7863   PetscErrorCode     ierr;
7864 
7865   PetscFunctionBegin;
7866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7867   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7868   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7869   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7870   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7871   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
7872 
7873   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
7874 
7875   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7876   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7877   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7878   if (ierr) {
7879     PetscMPIInt rank;
7880 
7881     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7882     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7883     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7884     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7885     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7886     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7887   }
7888   if (mesh->printFEM > 1) {
7889     PetscInt i;
7890     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7891     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7892     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7893   }
7894 
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   PetscFunctionReturn(PETSC_SUCCESS);
7898 }
7899 
7900 /*@C
7901   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7902 
7903   Not collective
7904 
7905   Input Parameters:
7906 + dm            - The `DM`
7907 . section       - The section describing the layout in `v`, or `NULL` to use the default section
7908 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7909 . A             - The matrix
7910 . point         - The point in the `DM`
7911 . values        - The array of values
7912 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7913 
7914   Level: intermediate
7915 
7916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7917 @*/
7918 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7919 {
7920   PetscFunctionBegin;
7921   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
7922   PetscFunctionReturn(PETSC_SUCCESS);
7923 }
7924 
7925 /*@C
7926   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
7927 
7928   Not collective
7929 
7930   Input Parameters:
7931 + dmRow            - The `DM` for the row fields
7932 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
7933 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
7934 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7935 . dmCol            - The `DM` for the column fields
7936 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
7937 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
7938 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7939 . A                - The matrix
7940 . point            - The point in the `DM`
7941 . values           - The array of values
7942 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7943 
7944   Level: intermediate
7945 
7946 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7947 @*/
7948 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)
7949 {
7950   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7951   PetscInt          *indicesRow, *indicesCol;
7952   PetscInt           numIndicesRow, numIndicesCol;
7953   const PetscScalar *valuesOrig = values;
7954   PetscErrorCode     ierr;
7955 
7956   PetscFunctionBegin;
7957   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7958   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7959   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7960   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7961   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7962   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
7963   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7964   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
7965   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7966   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
7967   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
7968 
7969   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7970   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7971 
7972   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7973   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7974   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7975   if (ierr) {
7976     PetscMPIInt rank;
7977 
7978     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7979     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7980     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7981     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7982     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7983     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7984   }
7985 
7986   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7987   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7988   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7989   PetscFunctionReturn(PETSC_SUCCESS);
7990 }
7991 
7992 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7993 {
7994   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7995   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7996   PetscInt       *cpoints = NULL;
7997   PetscInt       *findices, *cindices;
7998   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7999   PetscInt        foffsets[32], coffsets[32];
8000   DMPolytopeType  ct;
8001   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8002   PetscErrorCode  ierr;
8003 
8004   PetscFunctionBegin;
8005   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8006   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8007   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8008   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8009   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8010   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8011   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8012   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8013   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8014   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8015   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8016   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8017   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8018   PetscCall(PetscArrayzero(foffsets, 32));
8019   PetscCall(PetscArrayzero(coffsets, 32));
8020   /* Column indices */
8021   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8022   maxFPoints = numCPoints;
8023   /* Compress out points not in the section */
8024   /*   TODO: Squeeze out points with 0 dof as well */
8025   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8026   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8027     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8028       cpoints[q * 2]     = cpoints[p];
8029       cpoints[q * 2 + 1] = cpoints[p + 1];
8030       ++q;
8031     }
8032   }
8033   numCPoints = q;
8034   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8035     PetscInt fdof;
8036 
8037     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8038     if (!dof) continue;
8039     for (f = 0; f < numFields; ++f) {
8040       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8041       coffsets[f + 1] += fdof;
8042     }
8043     numCIndices += dof;
8044   }
8045   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8046   /* Row indices */
8047   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8048   {
8049     DMPlexTransform tr;
8050     DMPolytopeType *rct;
8051     PetscInt       *rsize, *rcone, *rornt, Nt;
8052 
8053     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8054     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8055     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8056     numSubcells = rsize[Nt - 1];
8057     PetscCall(DMPlexTransformDestroy(&tr));
8058   }
8059   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8060   for (r = 0, q = 0; r < numSubcells; ++r) {
8061     /* TODO Map from coarse to fine cells */
8062     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8063     /* Compress out points not in the section */
8064     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8065     for (p = 0; p < numFPoints * 2; p += 2) {
8066       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8067         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8068         if (!dof) continue;
8069         for (s = 0; s < q; ++s)
8070           if (fpoints[p] == ftotpoints[s * 2]) break;
8071         if (s < q) continue;
8072         ftotpoints[q * 2]     = fpoints[p];
8073         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8074         ++q;
8075       }
8076     }
8077     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8078   }
8079   numFPoints = q;
8080   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8081     PetscInt fdof;
8082 
8083     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8084     if (!dof) continue;
8085     for (f = 0; f < numFields; ++f) {
8086       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8087       foffsets[f + 1] += fdof;
8088     }
8089     numFIndices += dof;
8090   }
8091   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8092 
8093   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8094   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8095   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8096   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8097   if (numFields) {
8098     const PetscInt **permsF[32] = {NULL};
8099     const PetscInt **permsC[32] = {NULL};
8100 
8101     for (f = 0; f < numFields; f++) {
8102       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8103       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8104     }
8105     for (p = 0; p < numFPoints; p++) {
8106       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8107       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8108     }
8109     for (p = 0; p < numCPoints; p++) {
8110       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8111       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8112     }
8113     for (f = 0; f < numFields; f++) {
8114       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8115       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8116     }
8117   } else {
8118     const PetscInt **permsF = NULL;
8119     const PetscInt **permsC = NULL;
8120 
8121     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8122     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8123     for (p = 0, off = 0; p < numFPoints; p++) {
8124       const PetscInt *perm = permsF ? permsF[p] : NULL;
8125 
8126       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8127       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8128     }
8129     for (p = 0, off = 0; p < numCPoints; p++) {
8130       const PetscInt *perm = permsC ? permsC[p] : NULL;
8131 
8132       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8133       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8134     }
8135     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8136     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8137   }
8138   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8139   /* TODO: flips */
8140   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8141   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8142   if (ierr) {
8143     PetscMPIInt rank;
8144 
8145     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8146     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8147     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8148     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8149     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8150   }
8151   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8152   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8153   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8154   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8155   PetscFunctionReturn(PETSC_SUCCESS);
8156 }
8157 
8158 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8159 {
8160   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8161   PetscInt       *cpoints = NULL;
8162   PetscInt        foffsets[32], coffsets[32];
8163   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8164   DMPolytopeType  ct;
8165   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8166 
8167   PetscFunctionBegin;
8168   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8169   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8170   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8171   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8172   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8173   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8174   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8175   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8176   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8177   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8178   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8179   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8180   PetscCall(PetscArrayzero(foffsets, 32));
8181   PetscCall(PetscArrayzero(coffsets, 32));
8182   /* Column indices */
8183   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8184   maxFPoints = numCPoints;
8185   /* Compress out points not in the section */
8186   /*   TODO: Squeeze out points with 0 dof as well */
8187   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8188   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8189     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8190       cpoints[q * 2]     = cpoints[p];
8191       cpoints[q * 2 + 1] = cpoints[p + 1];
8192       ++q;
8193     }
8194   }
8195   numCPoints = q;
8196   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8197     PetscInt fdof;
8198 
8199     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8200     if (!dof) continue;
8201     for (f = 0; f < numFields; ++f) {
8202       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8203       coffsets[f + 1] += fdof;
8204     }
8205     numCIndices += dof;
8206   }
8207   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8208   /* Row indices */
8209   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8210   {
8211     DMPlexTransform tr;
8212     DMPolytopeType *rct;
8213     PetscInt       *rsize, *rcone, *rornt, Nt;
8214 
8215     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8216     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8217     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8218     numSubcells = rsize[Nt - 1];
8219     PetscCall(DMPlexTransformDestroy(&tr));
8220   }
8221   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8222   for (r = 0, q = 0; r < numSubcells; ++r) {
8223     /* TODO Map from coarse to fine cells */
8224     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8225     /* Compress out points not in the section */
8226     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8227     for (p = 0; p < numFPoints * 2; p += 2) {
8228       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8229         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8230         if (!dof) continue;
8231         for (s = 0; s < q; ++s)
8232           if (fpoints[p] == ftotpoints[s * 2]) break;
8233         if (s < q) continue;
8234         ftotpoints[q * 2]     = fpoints[p];
8235         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8236         ++q;
8237       }
8238     }
8239     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8240   }
8241   numFPoints = q;
8242   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8243     PetscInt fdof;
8244 
8245     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8246     if (!dof) continue;
8247     for (f = 0; f < numFields; ++f) {
8248       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8249       foffsets[f + 1] += fdof;
8250     }
8251     numFIndices += dof;
8252   }
8253   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8254 
8255   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8256   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8257   if (numFields) {
8258     const PetscInt **permsF[32] = {NULL};
8259     const PetscInt **permsC[32] = {NULL};
8260 
8261     for (f = 0; f < numFields; f++) {
8262       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8263       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8264     }
8265     for (p = 0; p < numFPoints; p++) {
8266       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8267       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8268     }
8269     for (p = 0; p < numCPoints; p++) {
8270       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8271       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8272     }
8273     for (f = 0; f < numFields; f++) {
8274       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8275       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8276     }
8277   } else {
8278     const PetscInt **permsF = NULL;
8279     const PetscInt **permsC = NULL;
8280 
8281     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8282     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8283     for (p = 0, off = 0; p < numFPoints; p++) {
8284       const PetscInt *perm = permsF ? permsF[p] : NULL;
8285 
8286       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8287       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8288     }
8289     for (p = 0, off = 0; p < numCPoints; p++) {
8290       const PetscInt *perm = permsC ? permsC[p] : NULL;
8291 
8292       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8293       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8294     }
8295     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8296     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8297   }
8298   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8299   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8300   PetscFunctionReturn(PETSC_SUCCESS);
8301 }
8302 
8303 /*@C
8304   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8305 
8306   Input Parameter:
8307 . dm - The `DMPLEX` object
8308 
8309   Output Parameter:
8310 . cellHeight - The height of a cell
8311 
8312   Level: developer
8313 
8314 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8315 @*/
8316 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8317 {
8318   DM_Plex *mesh = (DM_Plex *)dm->data;
8319 
8320   PetscFunctionBegin;
8321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8322   PetscAssertPointer(cellHeight, 2);
8323   *cellHeight = mesh->vtkCellHeight;
8324   PetscFunctionReturn(PETSC_SUCCESS);
8325 }
8326 
8327 /*@C
8328   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8329 
8330   Input Parameters:
8331 + dm         - The `DMPLEX` object
8332 - cellHeight - The height of a cell
8333 
8334   Level: developer
8335 
8336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8337 @*/
8338 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8339 {
8340   DM_Plex *mesh = (DM_Plex *)dm->data;
8341 
8342   PetscFunctionBegin;
8343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8344   mesh->vtkCellHeight = cellHeight;
8345   PetscFunctionReturn(PETSC_SUCCESS);
8346 }
8347 
8348 /*@
8349   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8350 
8351   Input Parameters:
8352 + dm - The `DMPLEX` object
8353 - ct - The `DMPolytopeType` of the cell
8354 
8355   Output Parameters:
8356 + start - The first cell of this type, or `NULL`
8357 - end   - The upper bound on this celltype, or `NULL`
8358 
8359   Level: advanced
8360 
8361 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8362 @*/
8363 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8364 {
8365   DM_Plex *mesh = (DM_Plex *)dm->data;
8366   DMLabel  label;
8367   PetscInt pStart, pEnd;
8368 
8369   PetscFunctionBegin;
8370   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8371   if (start) {
8372     PetscAssertPointer(start, 3);
8373     *start = 0;
8374   }
8375   if (end) {
8376     PetscAssertPointer(end, 4);
8377     *end = 0;
8378   }
8379   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8380   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8381   if (mesh->tr) {
8382     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8383   } else {
8384     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8385     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8386     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8387   }
8388   PetscFunctionReturn(PETSC_SUCCESS);
8389 }
8390 
8391 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8392 {
8393   PetscSection section, globalSection;
8394   PetscInt    *numbers, p;
8395 
8396   PetscFunctionBegin;
8397   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8398   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8399   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8400   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8401   PetscCall(PetscSectionSetUp(section));
8402   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8403   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8404   for (p = pStart; p < pEnd; ++p) {
8405     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8406     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8407     else numbers[p - pStart] += shift;
8408   }
8409   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8410   if (globalSize) {
8411     PetscLayout layout;
8412     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8413     PetscCall(PetscLayoutGetSize(layout, globalSize));
8414     PetscCall(PetscLayoutDestroy(&layout));
8415   }
8416   PetscCall(PetscSectionDestroy(&section));
8417   PetscCall(PetscSectionDestroy(&globalSection));
8418   PetscFunctionReturn(PETSC_SUCCESS);
8419 }
8420 
8421 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8422 {
8423   PetscInt cellHeight, cStart, cEnd;
8424 
8425   PetscFunctionBegin;
8426   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8427   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8428   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8429   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8430   PetscFunctionReturn(PETSC_SUCCESS);
8431 }
8432 
8433 /*@
8434   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8435 
8436   Input Parameter:
8437 . dm - The `DMPLEX` object
8438 
8439   Output Parameter:
8440 . globalCellNumbers - Global cell numbers for all cells on this process
8441 
8442   Level: developer
8443 
8444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8445 @*/
8446 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8447 {
8448   DM_Plex *mesh = (DM_Plex *)dm->data;
8449 
8450   PetscFunctionBegin;
8451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8452   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8453   *globalCellNumbers = mesh->globalCellNumbers;
8454   PetscFunctionReturn(PETSC_SUCCESS);
8455 }
8456 
8457 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8458 {
8459   PetscInt vStart, vEnd;
8460 
8461   PetscFunctionBegin;
8462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8463   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8464   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8465   PetscFunctionReturn(PETSC_SUCCESS);
8466 }
8467 
8468 /*@
8469   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8470 
8471   Input Parameter:
8472 . dm - The `DMPLEX` object
8473 
8474   Output Parameter:
8475 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8476 
8477   Level: developer
8478 
8479 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8480 @*/
8481 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8482 {
8483   DM_Plex *mesh = (DM_Plex *)dm->data;
8484 
8485   PetscFunctionBegin;
8486   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8487   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8488   *globalVertexNumbers = mesh->globalVertexNumbers;
8489   PetscFunctionReturn(PETSC_SUCCESS);
8490 }
8491 
8492 /*@
8493   DMPlexCreatePointNumbering - Create a global numbering for all points.
8494 
8495   Collective
8496 
8497   Input Parameter:
8498 . dm - The `DMPLEX` object
8499 
8500   Output Parameter:
8501 . globalPointNumbers - Global numbers for all points on this process
8502 
8503   Level: developer
8504 
8505   Notes:
8506   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8507   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8508   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8509   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8510 
8511   The partitioned mesh is
8512   ```
8513   (2)--0--(3)--1--(4)    (1)--0--(2)
8514   ```
8515   and its global numbering is
8516   ```
8517   (3)--0--(4)--1--(5)--2--(6)
8518   ```
8519   Then the global numbering is provided as
8520   ```
8521   [0] Number of indices in set 5
8522   [0] 0 0
8523   [0] 1 1
8524   [0] 2 3
8525   [0] 3 4
8526   [0] 4 -6
8527   [1] Number of indices in set 3
8528   [1] 0 2
8529   [1] 1 5
8530   [1] 2 6
8531   ```
8532 
8533 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8534 @*/
8535 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8536 {
8537   IS        nums[4];
8538   PetscInt  depths[4], gdepths[4], starts[4];
8539   PetscInt  depth, d, shift = 0;
8540   PetscBool empty = PETSC_FALSE;
8541 
8542   PetscFunctionBegin;
8543   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8544   PetscCall(DMPlexGetDepth(dm, &depth));
8545   // For unstratified meshes use dim instead of depth
8546   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8547   // If any stratum is empty, we must mark all empty
8548   for (d = 0; d <= depth; ++d) {
8549     PetscInt end;
8550 
8551     depths[d] = depth - d;
8552     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8553     if (!(starts[d] - end)) empty = PETSC_TRUE;
8554   }
8555   if (empty)
8556     for (d = 0; d <= depth; ++d) {
8557       depths[d] = -1;
8558       starts[d] = -1;
8559     }
8560   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8561   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8562   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]);
8563   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8564   for (d = 0; d <= depth; ++d) {
8565     PetscInt pStart, pEnd, gsize;
8566 
8567     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8568     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8569     shift += gsize;
8570   }
8571   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8572   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8573   PetscFunctionReturn(PETSC_SUCCESS);
8574 }
8575 
8576 /*@
8577   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8578 
8579   Input Parameter:
8580 . dm - The `DMPLEX` object
8581 
8582   Output Parameter:
8583 . ranks - The rank field
8584 
8585   Options Database Key:
8586 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8587 
8588   Level: intermediate
8589 
8590 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8591 @*/
8592 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8593 {
8594   DM             rdm;
8595   PetscFE        fe;
8596   PetscScalar   *r;
8597   PetscMPIInt    rank;
8598   DMPolytopeType ct;
8599   PetscInt       dim, cStart, cEnd, c;
8600   PetscBool      simplex;
8601 
8602   PetscFunctionBeginUser;
8603   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8604   PetscAssertPointer(ranks, 2);
8605   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8606   PetscCall(DMClone(dm, &rdm));
8607   PetscCall(DMGetDimension(rdm, &dim));
8608   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8609   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8610   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8611   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8612   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8613   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8614   PetscCall(PetscFEDestroy(&fe));
8615   PetscCall(DMCreateDS(rdm));
8616   PetscCall(DMCreateGlobalVector(rdm, ranks));
8617   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8618   PetscCall(VecGetArray(*ranks, &r));
8619   for (c = cStart; c < cEnd; ++c) {
8620     PetscScalar *lr;
8621 
8622     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8623     if (lr) *lr = rank;
8624   }
8625   PetscCall(VecRestoreArray(*ranks, &r));
8626   PetscCall(DMDestroy(&rdm));
8627   PetscFunctionReturn(PETSC_SUCCESS);
8628 }
8629 
8630 /*@
8631   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8632 
8633   Input Parameters:
8634 + dm    - The `DMPLEX`
8635 - label - The `DMLabel`
8636 
8637   Output Parameter:
8638 . val - The label value field
8639 
8640   Options Database Key:
8641 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8642 
8643   Level: intermediate
8644 
8645 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8646 @*/
8647 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8648 {
8649   DM           rdm;
8650   PetscFE      fe;
8651   PetscScalar *v;
8652   PetscInt     dim, cStart, cEnd, c;
8653 
8654   PetscFunctionBeginUser;
8655   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8656   PetscAssertPointer(label, 2);
8657   PetscAssertPointer(val, 3);
8658   PetscCall(DMClone(dm, &rdm));
8659   PetscCall(DMGetDimension(rdm, &dim));
8660   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8661   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8662   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8663   PetscCall(PetscFEDestroy(&fe));
8664   PetscCall(DMCreateDS(rdm));
8665   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8666   PetscCall(DMCreateGlobalVector(rdm, val));
8667   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8668   PetscCall(VecGetArray(*val, &v));
8669   for (c = cStart; c < cEnd; ++c) {
8670     PetscScalar *lv;
8671     PetscInt     cval;
8672 
8673     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8674     PetscCall(DMLabelGetValue(label, c, &cval));
8675     *lv = cval;
8676   }
8677   PetscCall(VecRestoreArray(*val, &v));
8678   PetscCall(DMDestroy(&rdm));
8679   PetscFunctionReturn(PETSC_SUCCESS);
8680 }
8681 
8682 /*@
8683   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8684 
8685   Input Parameter:
8686 . dm - The `DMPLEX` object
8687 
8688   Level: developer
8689 
8690   Notes:
8691   This is a useful diagnostic when creating meshes programmatically.
8692 
8693   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8694 
8695 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8696 @*/
8697 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8698 {
8699   PetscSection    coneSection, supportSection;
8700   const PetscInt *cone, *support;
8701   PetscInt        coneSize, c, supportSize, s;
8702   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8703   PetscBool       storagecheck = PETSC_TRUE;
8704 
8705   PetscFunctionBegin;
8706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8707   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8708   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8709   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8710   /* Check that point p is found in the support of its cone points, and vice versa */
8711   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8712   for (p = pStart; p < pEnd; ++p) {
8713     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8714     PetscCall(DMPlexGetCone(dm, p, &cone));
8715     for (c = 0; c < coneSize; ++c) {
8716       PetscBool dup = PETSC_FALSE;
8717       PetscInt  d;
8718       for (d = c - 1; d >= 0; --d) {
8719         if (cone[c] == cone[d]) {
8720           dup = PETSC_TRUE;
8721           break;
8722         }
8723       }
8724       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8725       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8726       for (s = 0; s < supportSize; ++s) {
8727         if (support[s] == p) break;
8728       }
8729       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8730         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8731         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8732         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8733         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8734         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8735         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8736         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]);
8737         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8738       }
8739     }
8740     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8741     if (p != pp) {
8742       storagecheck = PETSC_FALSE;
8743       continue;
8744     }
8745     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8746     PetscCall(DMPlexGetSupport(dm, p, &support));
8747     for (s = 0; s < supportSize; ++s) {
8748       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8749       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8750       for (c = 0; c < coneSize; ++c) {
8751         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8752         if (cone[c] != pp) {
8753           c = 0;
8754           break;
8755         }
8756         if (cone[c] == p) break;
8757       }
8758       if (c >= coneSize) {
8759         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8760         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8761         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8762         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8763         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8764         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8765         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8766       }
8767     }
8768   }
8769   if (storagecheck) {
8770     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8771     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8772     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8773   }
8774   PetscFunctionReturn(PETSC_SUCCESS);
8775 }
8776 
8777 /*
8778   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.
8779 */
8780 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8781 {
8782   DMPolytopeType  cct;
8783   PetscInt        ptpoints[4];
8784   const PetscInt *cone, *ccone, *ptcone;
8785   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8786 
8787   PetscFunctionBegin;
8788   *unsplit = 0;
8789   switch (ct) {
8790   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8791     ptpoints[npt++] = c;
8792     break;
8793   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8794     PetscCall(DMPlexGetCone(dm, c, &cone));
8795     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8796     for (cp = 0; cp < coneSize; ++cp) {
8797       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8798       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8799     }
8800     break;
8801   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8802   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8803     PetscCall(DMPlexGetCone(dm, c, &cone));
8804     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8805     for (cp = 0; cp < coneSize; ++cp) {
8806       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8807       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8808       for (ccp = 0; ccp < cconeSize; ++ccp) {
8809         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8810         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8811           PetscInt p;
8812           for (p = 0; p < npt; ++p)
8813             if (ptpoints[p] == ccone[ccp]) break;
8814           if (p == npt) ptpoints[npt++] = ccone[ccp];
8815         }
8816       }
8817     }
8818     break;
8819   default:
8820     break;
8821   }
8822   for (pt = 0; pt < npt; ++pt) {
8823     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8824     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8825   }
8826   PetscFunctionReturn(PETSC_SUCCESS);
8827 }
8828 
8829 /*@
8830   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8831 
8832   Input Parameters:
8833 + dm         - The `DMPLEX` object
8834 - cellHeight - Normally 0
8835 
8836   Level: developer
8837 
8838   Notes:
8839   This is a useful diagnostic when creating meshes programmatically.
8840   Currently applicable only to homogeneous simplex or tensor meshes.
8841 
8842   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8843 
8844 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8845 @*/
8846 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8847 {
8848   DMPlexInterpolatedFlag interp;
8849   DMPolytopeType         ct;
8850   PetscInt               vStart, vEnd, cStart, cEnd, c;
8851 
8852   PetscFunctionBegin;
8853   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8854   PetscCall(DMPlexIsInterpolated(dm, &interp));
8855   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8856   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8857   for (c = cStart; c < cEnd; ++c) {
8858     PetscInt *closure = NULL;
8859     PetscInt  coneSize, closureSize, cl, Nv = 0;
8860 
8861     PetscCall(DMPlexGetCellType(dm, c, &ct));
8862     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8863     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8864     if (interp == DMPLEX_INTERPOLATED_FULL) {
8865       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8866       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));
8867     }
8868     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8869     for (cl = 0; cl < closureSize * 2; cl += 2) {
8870       const PetscInt p = closure[cl];
8871       if ((p >= vStart) && (p < vEnd)) ++Nv;
8872     }
8873     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8874     /* Special Case: Tensor faces with identified vertices */
8875     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8876       PetscInt unsplit;
8877 
8878       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8879       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8880     }
8881     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));
8882   }
8883   PetscFunctionReturn(PETSC_SUCCESS);
8884 }
8885 
8886 /*@
8887   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8888 
8889   Collective
8890 
8891   Input Parameters:
8892 + dm         - The `DMPLEX` object
8893 - cellHeight - Normally 0
8894 
8895   Level: developer
8896 
8897   Notes:
8898   This is a useful diagnostic when creating meshes programmatically.
8899   This routine is only relevant for meshes that are fully interpolated across all ranks.
8900   It will error out if a partially interpolated mesh is given on some rank.
8901   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8902 
8903   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8904 
8905 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8906 @*/
8907 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8908 {
8909   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8910   DMPlexInterpolatedFlag interpEnum;
8911 
8912   PetscFunctionBegin;
8913   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8914   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8915   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8916   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8917     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8918     PetscFunctionReturn(PETSC_SUCCESS);
8919   }
8920 
8921   PetscCall(DMGetDimension(dm, &dim));
8922   PetscCall(DMPlexGetDepth(dm, &depth));
8923   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8924   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8925     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8926     for (c = cStart; c < cEnd; ++c) {
8927       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8928       const DMPolytopeType *faceTypes;
8929       DMPolytopeType        ct;
8930       PetscInt              numFaces, coneSize, f;
8931       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8932 
8933       PetscCall(DMPlexGetCellType(dm, c, &ct));
8934       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8935       if (unsplit) continue;
8936       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8937       PetscCall(DMPlexGetCone(dm, c, &cone));
8938       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8939       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8940       for (cl = 0; cl < closureSize * 2; cl += 2) {
8941         const PetscInt p = closure[cl];
8942         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8943       }
8944       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8945       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);
8946       for (f = 0; f < numFaces; ++f) {
8947         DMPolytopeType fct;
8948         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8949 
8950         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8951         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8952         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8953           const PetscInt p = fclosure[cl];
8954           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8955         }
8956         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]);
8957         for (v = 0; v < fnumCorners; ++v) {
8958           if (fclosure[v] != faces[fOff + v]) {
8959             PetscInt v1;
8960 
8961             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8962             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8963             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8964             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8965             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8966             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]);
8967           }
8968         }
8969         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8970         fOff += faceSizes[f];
8971       }
8972       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8973       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8974     }
8975   }
8976   PetscFunctionReturn(PETSC_SUCCESS);
8977 }
8978 
8979 /*@
8980   DMPlexCheckGeometry - Check the geometry of mesh cells
8981 
8982   Input Parameter:
8983 . dm - The `DMPLEX` object
8984 
8985   Level: developer
8986 
8987   Notes:
8988   This is a useful diagnostic when creating meshes programmatically.
8989 
8990   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8991 
8992 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8993 @*/
8994 PetscErrorCode DMPlexCheckGeometry(DM dm)
8995 {
8996   Vec       coordinates;
8997   PetscReal detJ, J[9], refVol = 1.0;
8998   PetscReal vol;
8999   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9000 
9001   PetscFunctionBegin;
9002   PetscCall(DMGetDimension(dm, &dim));
9003   PetscCall(DMGetCoordinateDim(dm, &dE));
9004   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9005   PetscCall(DMPlexGetDepth(dm, &depth));
9006   for (d = 0; d < dim; ++d) refVol *= 2.0;
9007   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9008   /* Make sure local coordinates are created, because that step is collective */
9009   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9010   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9011   for (c = cStart; c < cEnd; ++c) {
9012     DMPolytopeType ct;
9013     PetscInt       unsplit;
9014     PetscBool      ignoreZeroVol = PETSC_FALSE;
9015 
9016     PetscCall(DMPlexGetCellType(dm, c, &ct));
9017     switch (ct) {
9018     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9019     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9020     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9021       ignoreZeroVol = PETSC_TRUE;
9022       break;
9023     default:
9024       break;
9025     }
9026     switch (ct) {
9027     case DM_POLYTOPE_TRI_PRISM:
9028     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9029     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9030     case DM_POLYTOPE_PYRAMID:
9031       continue;
9032     default:
9033       break;
9034     }
9035     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9036     if (unsplit) continue;
9037     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9038     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);
9039     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9040     /* This should work with periodicity since DG coordinates should be used */
9041     if (depth > 1) {
9042       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9043       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);
9044       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9045     }
9046   }
9047   PetscFunctionReturn(PETSC_SUCCESS);
9048 }
9049 
9050 /*@
9051   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9052 
9053   Collective
9054 
9055   Input Parameters:
9056 + dm              - The `DMPLEX` object
9057 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9058 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9059 
9060   Level: developer
9061 
9062   Notes:
9063   This is mainly intended for debugging/testing purposes.
9064 
9065   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9066 
9067   Extra roots can come from periodic cuts, where additional points appear on the boundary
9068 
9069 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9070 @*/
9071 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9072 {
9073   PetscInt           l, nleaves, nroots, overlap;
9074   const PetscInt    *locals;
9075   const PetscSFNode *remotes;
9076   PetscBool          distributed;
9077   MPI_Comm           comm;
9078   PetscMPIInt        rank;
9079 
9080   PetscFunctionBegin;
9081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9082   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9083   else pointSF = dm->sf;
9084   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9085   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9086   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9087   {
9088     PetscMPIInt mpiFlag;
9089 
9090     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9091     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9092   }
9093   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9094   PetscCall(DMPlexIsDistributed(dm, &distributed));
9095   if (!distributed) {
9096     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);
9097     PetscFunctionReturn(PETSC_SUCCESS);
9098   }
9099   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);
9100   PetscCall(DMPlexGetOverlap(dm, &overlap));
9101 
9102   /* Check SF graph is compatible with DMPlex chart */
9103   {
9104     PetscInt pStart, pEnd, maxLeaf;
9105 
9106     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9107     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9108     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9109     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9110   }
9111 
9112   /* Check Point SF has no local points referenced */
9113   for (l = 0; l < nleaves; l++) {
9114     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);
9115   }
9116 
9117   /* Check there are no cells in interface */
9118   if (!overlap) {
9119     PetscInt cellHeight, cStart, cEnd;
9120 
9121     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9122     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9123     for (l = 0; l < nleaves; ++l) {
9124       const PetscInt point = locals ? locals[l] : l;
9125 
9126       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9127     }
9128   }
9129 
9130   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9131   {
9132     const PetscInt *rootdegree;
9133 
9134     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9135     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9136     for (l = 0; l < nleaves; ++l) {
9137       const PetscInt  point = locals ? locals[l] : l;
9138       const PetscInt *cone;
9139       PetscInt        coneSize, c, idx;
9140 
9141       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9142       PetscCall(DMPlexGetCone(dm, point, &cone));
9143       for (c = 0; c < coneSize; ++c) {
9144         if (!rootdegree[cone[c]]) {
9145           if (locals) {
9146             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9147           } else {
9148             idx = (cone[c] < nleaves) ? cone[c] : -1;
9149           }
9150           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9151         }
9152       }
9153     }
9154   }
9155   PetscFunctionReturn(PETSC_SUCCESS);
9156 }
9157 
9158 /*@
9159   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9160 
9161   Input Parameter:
9162 . dm - The `DMPLEX` object
9163 
9164   Level: developer
9165 
9166   Notes:
9167   This is a useful diagnostic when creating meshes programmatically.
9168 
9169   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9170 
9171   Currently does not include `DMPlexCheckCellShape()`.
9172 
9173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9174 @*/
9175 PetscErrorCode DMPlexCheck(DM dm)
9176 {
9177   PetscInt cellHeight;
9178 
9179   PetscFunctionBegin;
9180   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9181   PetscCall(DMPlexCheckSymmetry(dm));
9182   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9183   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9184   PetscCall(DMPlexCheckGeometry(dm));
9185   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9186   PetscCall(DMPlexCheckInterfaceCones(dm));
9187   PetscFunctionReturn(PETSC_SUCCESS);
9188 }
9189 
9190 typedef struct cell_stats {
9191   PetscReal min, max, sum, squaresum;
9192   PetscInt  count;
9193 } cell_stats_t;
9194 
9195 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9196 {
9197   PetscInt i, N = *len;
9198 
9199   for (i = 0; i < N; i++) {
9200     cell_stats_t *A = (cell_stats_t *)a;
9201     cell_stats_t *B = (cell_stats_t *)b;
9202 
9203     B->min = PetscMin(A->min, B->min);
9204     B->max = PetscMax(A->max, B->max);
9205     B->sum += A->sum;
9206     B->squaresum += A->squaresum;
9207     B->count += A->count;
9208   }
9209 }
9210 
9211 /*@
9212   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9213 
9214   Collective
9215 
9216   Input Parameters:
9217 + dm        - The `DMPLEX` object
9218 . output    - If true, statistics will be displayed on `stdout`
9219 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9220 
9221   Level: developer
9222 
9223   Notes:
9224   This is mainly intended for debugging/testing purposes.
9225 
9226   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9227 
9228 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9229 @*/
9230 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9231 {
9232   DM           dmCoarse;
9233   cell_stats_t stats, globalStats;
9234   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9235   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9236   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9237   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9238   PetscMPIInt  rank, size;
9239 
9240   PetscFunctionBegin;
9241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9242   stats.min = PETSC_MAX_REAL;
9243   stats.max = PETSC_MIN_REAL;
9244   stats.sum = stats.squaresum = 0.;
9245   stats.count                 = 0;
9246 
9247   PetscCallMPI(MPI_Comm_size(comm, &size));
9248   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9249   PetscCall(DMGetCoordinateDim(dm, &cdim));
9250   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9251   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9252   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9253   for (c = cStart; c < cEnd; c++) {
9254     PetscInt  i;
9255     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9256 
9257     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9258     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9259     for (i = 0; i < PetscSqr(cdim); ++i) {
9260       frobJ += J[i] * J[i];
9261       frobInvJ += invJ[i] * invJ[i];
9262     }
9263     cond2 = frobJ * frobInvJ;
9264     cond  = PetscSqrtReal(cond2);
9265 
9266     stats.min = PetscMin(stats.min, cond);
9267     stats.max = PetscMax(stats.max, cond);
9268     stats.sum += cond;
9269     stats.squaresum += cond2;
9270     stats.count++;
9271     if (output && cond > limit) {
9272       PetscSection coordSection;
9273       Vec          coordsLocal;
9274       PetscScalar *coords = NULL;
9275       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9276 
9277       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9278       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9279       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9280       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9281       for (i = 0; i < Nv / cdim; ++i) {
9282         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9283         for (d = 0; d < cdim; ++d) {
9284           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9285           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9286         }
9287         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9288       }
9289       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9290       for (cl = 0; cl < clSize * 2; cl += 2) {
9291         const PetscInt edge = closure[cl];
9292 
9293         if ((edge >= eStart) && (edge < eEnd)) {
9294           PetscReal len;
9295 
9296           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9297           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9298         }
9299       }
9300       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9301       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9302     }
9303   }
9304   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9305 
9306   if (size > 1) {
9307     PetscMPIInt  blockLengths[2] = {4, 1};
9308     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9309     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9310     MPI_Op       statReduce;
9311 
9312     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9313     PetscCallMPI(MPI_Type_commit(&statType));
9314     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9315     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9316     PetscCallMPI(MPI_Op_free(&statReduce));
9317     PetscCallMPI(MPI_Type_free(&statType));
9318   } else {
9319     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9320   }
9321   if (rank == 0) {
9322     count = globalStats.count;
9323     min   = globalStats.min;
9324     max   = globalStats.max;
9325     mean  = globalStats.sum / globalStats.count;
9326     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9327   }
9328 
9329   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));
9330   PetscCall(PetscFree2(J, invJ));
9331 
9332   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9333   if (dmCoarse) {
9334     PetscBool isplex;
9335 
9336     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9337     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9338   }
9339   PetscFunctionReturn(PETSC_SUCCESS);
9340 }
9341 
9342 /*@
9343   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9344   orthogonal quality below given tolerance.
9345 
9346   Collective
9347 
9348   Input Parameters:
9349 + dm   - The `DMPLEX` object
9350 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9351 - atol - [0, 1] Absolute tolerance for tagging cells.
9352 
9353   Output Parameters:
9354 + OrthQual      - `Vec` containing orthogonal quality per cell
9355 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9356 
9357   Options Database Keys:
9358 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9359 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9360 
9361   Level: intermediate
9362 
9363   Notes:
9364   Orthogonal quality is given by the following formula\:
9365 
9366   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9367 
9368   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
9369   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9370   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9371   calculating the cosine of the angle between these vectors.
9372 
9373   Orthogonal quality ranges from 1 (best) to 0 (worst).
9374 
9375   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9376   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9377 
9378   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9379 
9380 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9381 @*/
9382 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9383 {
9384   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9385   PetscInt              *idx;
9386   PetscScalar           *oqVals;
9387   const PetscScalar     *cellGeomArr, *faceGeomArr;
9388   PetscReal             *ci, *fi, *Ai;
9389   MPI_Comm               comm;
9390   Vec                    cellgeom, facegeom;
9391   DM                     dmFace, dmCell;
9392   IS                     glob;
9393   ISLocalToGlobalMapping ltog;
9394   PetscViewer            vwr;
9395 
9396   PetscFunctionBegin;
9397   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9398   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9399   PetscAssertPointer(OrthQual, 4);
9400   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9401   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9402   PetscCall(DMGetDimension(dm, &nc));
9403   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9404   {
9405     DMPlexInterpolatedFlag interpFlag;
9406 
9407     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9408     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9409       PetscMPIInt rank;
9410 
9411       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9412       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9413     }
9414   }
9415   if (OrthQualLabel) {
9416     PetscAssertPointer(OrthQualLabel, 5);
9417     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9418     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9419   } else {
9420     *OrthQualLabel = NULL;
9421   }
9422   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9423   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9424   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9425   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9426   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9427   PetscCall(VecCreate(comm, OrthQual));
9428   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9429   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9430   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9431   PetscCall(VecSetUp(*OrthQual));
9432   PetscCall(ISDestroy(&glob));
9433   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9434   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9435   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9436   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9437   PetscCall(VecGetDM(cellgeom, &dmCell));
9438   PetscCall(VecGetDM(facegeom, &dmFace));
9439   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9440   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9441     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9442     PetscInt         cellarr[2], *adj = NULL;
9443     PetscScalar     *cArr, *fArr;
9444     PetscReal        minvalc = 1.0, minvalf = 1.0;
9445     PetscFVCellGeom *cg;
9446 
9447     idx[cellIter] = cell - cStart;
9448     cellarr[0]    = cell;
9449     /* Make indexing into cellGeom easier */
9450     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9451     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9452     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9453     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9454     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9455       PetscInt         i;
9456       const PetscInt   neigh  = adj[cellneigh];
9457       PetscReal        normci = 0, normfi = 0, normai = 0;
9458       PetscFVCellGeom *cgneigh;
9459       PetscFVFaceGeom *fg;
9460 
9461       /* Don't count ourselves in the neighbor list */
9462       if (neigh == cell) continue;
9463       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9464       cellarr[1] = neigh;
9465       {
9466         PetscInt        numcovpts;
9467         const PetscInt *covpts;
9468 
9469         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9470         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9471         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9472       }
9473 
9474       /* Compute c_i, f_i and their norms */
9475       for (i = 0; i < nc; i++) {
9476         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9477         fi[i] = fg->centroid[i] - cg->centroid[i];
9478         Ai[i] = fg->normal[i];
9479         normci += PetscPowReal(ci[i], 2);
9480         normfi += PetscPowReal(fi[i], 2);
9481         normai += PetscPowReal(Ai[i], 2);
9482       }
9483       normci = PetscSqrtReal(normci);
9484       normfi = PetscSqrtReal(normfi);
9485       normai = PetscSqrtReal(normai);
9486 
9487       /* Normalize and compute for each face-cell-normal pair */
9488       for (i = 0; i < nc; i++) {
9489         ci[i] = ci[i] / normci;
9490         fi[i] = fi[i] / normfi;
9491         Ai[i] = Ai[i] / normai;
9492         /* PetscAbs because I don't know if normals are guaranteed to point out */
9493         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9494         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9495       }
9496       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9497       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9498     }
9499     PetscCall(PetscFree(adj));
9500     PetscCall(PetscFree2(cArr, fArr));
9501     /* Defer to cell if they're equal */
9502     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9503     if (OrthQualLabel) {
9504       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9505     }
9506   }
9507   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9508   PetscCall(VecAssemblyBegin(*OrthQual));
9509   PetscCall(VecAssemblyEnd(*OrthQual));
9510   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9511   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9512   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9513   if (OrthQualLabel) {
9514     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9515   }
9516   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9517   PetscCall(PetscViewerDestroy(&vwr));
9518   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9519   PetscFunctionReturn(PETSC_SUCCESS);
9520 }
9521 
9522 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9523  * interpolator construction */
9524 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9525 {
9526   PetscSection section, newSection, gsection;
9527   PetscSF      sf;
9528   PetscBool    hasConstraints, ghasConstraints;
9529 
9530   PetscFunctionBegin;
9531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9532   PetscAssertPointer(odm, 2);
9533   PetscCall(DMGetLocalSection(dm, &section));
9534   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9535   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9536   if (!ghasConstraints) {
9537     PetscCall(PetscObjectReference((PetscObject)dm));
9538     *odm = dm;
9539     PetscFunctionReturn(PETSC_SUCCESS);
9540   }
9541   PetscCall(DMClone(dm, odm));
9542   PetscCall(DMCopyFields(dm, *odm));
9543   PetscCall(DMGetLocalSection(*odm, &newSection));
9544   PetscCall(DMGetPointSF(*odm, &sf));
9545   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9546   PetscCall(DMSetGlobalSection(*odm, gsection));
9547   PetscCall(PetscSectionDestroy(&gsection));
9548   PetscFunctionReturn(PETSC_SUCCESS);
9549 }
9550 
9551 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9552 {
9553   DM        dmco, dmfo;
9554   Mat       interpo;
9555   Vec       rscale;
9556   Vec       cglobalo, clocal;
9557   Vec       fglobal, fglobalo, flocal;
9558   PetscBool regular;
9559 
9560   PetscFunctionBegin;
9561   PetscCall(DMGetFullDM(dmc, &dmco));
9562   PetscCall(DMGetFullDM(dmf, &dmfo));
9563   PetscCall(DMSetCoarseDM(dmfo, dmco));
9564   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9565   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9566   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9567   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9568   PetscCall(DMCreateLocalVector(dmc, &clocal));
9569   PetscCall(VecSet(cglobalo, 0.));
9570   PetscCall(VecSet(clocal, 0.));
9571   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9572   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9573   PetscCall(DMCreateLocalVector(dmf, &flocal));
9574   PetscCall(VecSet(fglobal, 0.));
9575   PetscCall(VecSet(fglobalo, 0.));
9576   PetscCall(VecSet(flocal, 0.));
9577   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9578   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9579   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9580   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9581   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9582   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9583   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9584   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9585   *shift = fglobal;
9586   PetscCall(VecDestroy(&flocal));
9587   PetscCall(VecDestroy(&fglobalo));
9588   PetscCall(VecDestroy(&clocal));
9589   PetscCall(VecDestroy(&cglobalo));
9590   PetscCall(VecDestroy(&rscale));
9591   PetscCall(MatDestroy(&interpo));
9592   PetscCall(DMDestroy(&dmfo));
9593   PetscCall(DMDestroy(&dmco));
9594   PetscFunctionReturn(PETSC_SUCCESS);
9595 }
9596 
9597 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9598 {
9599   PetscObject shifto;
9600   Vec         shift;
9601 
9602   PetscFunctionBegin;
9603   if (!interp) {
9604     Vec rscale;
9605 
9606     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9607     PetscCall(VecDestroy(&rscale));
9608   } else {
9609     PetscCall(PetscObjectReference((PetscObject)interp));
9610   }
9611   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9612   if (!shifto) {
9613     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9614     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9615     shifto = (PetscObject)shift;
9616     PetscCall(VecDestroy(&shift));
9617   }
9618   shift = (Vec)shifto;
9619   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9620   PetscCall(VecAXPY(fineSol, 1.0, shift));
9621   PetscCall(MatDestroy(&interp));
9622   PetscFunctionReturn(PETSC_SUCCESS);
9623 }
9624 
9625 /* Pointwise interpolation
9626      Just code FEM for now
9627      u^f = I u^c
9628      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9629      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9630      I_{ij} = psi^f_i phi^c_j
9631 */
9632 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9633 {
9634   PetscSection gsc, gsf;
9635   PetscInt     m, n;
9636   void        *ctx;
9637   DM           cdm;
9638   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9639 
9640   PetscFunctionBegin;
9641   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9642   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9643   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9644   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9645 
9646   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9647   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9648   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9649   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9650   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9651 
9652   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9653   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9654   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9655   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9656   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9657   if (scaling) {
9658     /* Use naive scaling */
9659     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9660   }
9661   PetscFunctionReturn(PETSC_SUCCESS);
9662 }
9663 
9664 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9665 {
9666   VecScatter ctx;
9667 
9668   PetscFunctionBegin;
9669   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9670   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9671   PetscCall(VecScatterDestroy(&ctx));
9672   PetscFunctionReturn(PETSC_SUCCESS);
9673 }
9674 
9675 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[])
9676 {
9677   const PetscInt Nc = uOff[1] - uOff[0];
9678   PetscInt       c;
9679   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9680 }
9681 
9682 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9683 {
9684   DM           dmc;
9685   PetscDS      ds;
9686   Vec          ones, locmass;
9687   IS           cellIS;
9688   PetscFormKey key;
9689   PetscInt     depth;
9690 
9691   PetscFunctionBegin;
9692   PetscCall(DMClone(dm, &dmc));
9693   PetscCall(DMCopyDisc(dm, dmc));
9694   PetscCall(DMGetDS(dmc, &ds));
9695   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9696   PetscCall(DMCreateGlobalVector(dmc, mass));
9697   PetscCall(DMGetLocalVector(dmc, &ones));
9698   PetscCall(DMGetLocalVector(dmc, &locmass));
9699   PetscCall(DMPlexGetDepth(dmc, &depth));
9700   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9701   PetscCall(VecSet(locmass, 0.0));
9702   PetscCall(VecSet(ones, 1.0));
9703   key.label = NULL;
9704   key.value = 0;
9705   key.field = 0;
9706   key.part  = 0;
9707   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9708   PetscCall(ISDestroy(&cellIS));
9709   PetscCall(VecSet(*mass, 0.0));
9710   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9711   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9712   PetscCall(DMRestoreLocalVector(dmc, &ones));
9713   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9714   PetscCall(DMDestroy(&dmc));
9715   PetscFunctionReturn(PETSC_SUCCESS);
9716 }
9717 
9718 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9719 {
9720   PetscSection gsc, gsf;
9721   PetscInt     m, n;
9722   void        *ctx;
9723   DM           cdm;
9724   PetscBool    regular;
9725 
9726   PetscFunctionBegin;
9727   if (dmFine == dmCoarse) {
9728     DM            dmc;
9729     PetscDS       ds;
9730     PetscWeakForm wf;
9731     Vec           u;
9732     IS            cellIS;
9733     PetscFormKey  key;
9734     PetscInt      depth;
9735 
9736     PetscCall(DMClone(dmFine, &dmc));
9737     PetscCall(DMCopyDisc(dmFine, dmc));
9738     PetscCall(DMGetDS(dmc, &ds));
9739     PetscCall(PetscDSGetWeakForm(ds, &wf));
9740     PetscCall(PetscWeakFormClear(wf));
9741     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9742     PetscCall(DMCreateMatrix(dmc, mass));
9743     PetscCall(DMGetLocalVector(dmc, &u));
9744     PetscCall(DMPlexGetDepth(dmc, &depth));
9745     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9746     PetscCall(MatZeroEntries(*mass));
9747     key.label = NULL;
9748     key.value = 0;
9749     key.field = 0;
9750     key.part  = 0;
9751     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9752     PetscCall(ISDestroy(&cellIS));
9753     PetscCall(DMRestoreLocalVector(dmc, &u));
9754     PetscCall(DMDestroy(&dmc));
9755   } else {
9756     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9757     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9758     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9759     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9760 
9761     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9762     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9763     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9764     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9765 
9766     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9767     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9768     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9769     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9770   }
9771   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9772   PetscFunctionReturn(PETSC_SUCCESS);
9773 }
9774 
9775 /*@
9776   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9777 
9778   Input Parameter:
9779 . dm - The `DMPLEX` object
9780 
9781   Output Parameter:
9782 . regular - The flag
9783 
9784   Level: intermediate
9785 
9786 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9787 @*/
9788 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9789 {
9790   PetscFunctionBegin;
9791   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9792   PetscAssertPointer(regular, 2);
9793   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9794   PetscFunctionReturn(PETSC_SUCCESS);
9795 }
9796 
9797 /*@
9798   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9799 
9800   Input Parameters:
9801 + dm      - The `DMPLEX` object
9802 - regular - The flag
9803 
9804   Level: intermediate
9805 
9806 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9807 @*/
9808 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9809 {
9810   PetscFunctionBegin;
9811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9812   ((DM_Plex *)dm->data)->regularRefinement = regular;
9813   PetscFunctionReturn(PETSC_SUCCESS);
9814 }
9815 
9816 /*@
9817   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9818   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9819 
9820   Not Collective
9821 
9822   Input Parameter:
9823 . dm - The `DMPLEX` object
9824 
9825   Output Parameters:
9826 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9827 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9828 
9829   Level: intermediate
9830 
9831 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9832 @*/
9833 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9834 {
9835   DM_Plex *plex = (DM_Plex *)dm->data;
9836 
9837   PetscFunctionBegin;
9838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9839   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9840   if (anchorSection) *anchorSection = plex->anchorSection;
9841   if (anchorIS) *anchorIS = plex->anchorIS;
9842   PetscFunctionReturn(PETSC_SUCCESS);
9843 }
9844 
9845 /*@
9846   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
9847 
9848   Collective
9849 
9850   Input Parameters:
9851 + dm            - The `DMPLEX` object
9852 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9853                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9854 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9855 
9856   Level: intermediate
9857 
9858   Notes:
9859   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
9860   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
9861   combination of other points' degrees of freedom.
9862 
9863   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9864   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9865 
9866   The reference counts of `anchorSection` and `anchorIS` are incremented.
9867 
9868 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9869 @*/
9870 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9871 {
9872   DM_Plex    *plex = (DM_Plex *)dm->data;
9873   PetscMPIInt result;
9874 
9875   PetscFunctionBegin;
9876   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9877   if (anchorSection) {
9878     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9879     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9880     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9881   }
9882   if (anchorIS) {
9883     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9884     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9885     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9886   }
9887 
9888   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9889   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9890   plex->anchorSection = anchorSection;
9891 
9892   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9893   PetscCall(ISDestroy(&plex->anchorIS));
9894   plex->anchorIS = anchorIS;
9895 
9896   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9897     PetscInt        size, a, pStart, pEnd;
9898     const PetscInt *anchors;
9899 
9900     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9901     PetscCall(ISGetLocalSize(anchorIS, &size));
9902     PetscCall(ISGetIndices(anchorIS, &anchors));
9903     for (a = 0; a < size; a++) {
9904       PetscInt p;
9905 
9906       p = anchors[a];
9907       if (p >= pStart && p < pEnd) {
9908         PetscInt dof;
9909 
9910         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9911         if (dof) {
9912           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9913           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9914         }
9915       }
9916     }
9917     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9918   }
9919   /* reset the generic constraints */
9920   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9921   PetscFunctionReturn(PETSC_SUCCESS);
9922 }
9923 
9924 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9925 {
9926   PetscSection anchorSection;
9927   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9928 
9929   PetscFunctionBegin;
9930   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9931   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9932   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9933   PetscCall(PetscSectionGetNumFields(section, &numFields));
9934   if (numFields) {
9935     PetscInt f;
9936     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9937 
9938     for (f = 0; f < numFields; f++) {
9939       PetscInt numComp;
9940 
9941       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9942       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9943     }
9944   }
9945   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9946   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9947   pStart = PetscMax(pStart, sStart);
9948   pEnd   = PetscMin(pEnd, sEnd);
9949   pEnd   = PetscMax(pStart, pEnd);
9950   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9951   for (p = pStart; p < pEnd; p++) {
9952     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9953     if (dof) {
9954       PetscCall(PetscSectionGetDof(section, p, &dof));
9955       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9956       for (f = 0; f < numFields; f++) {
9957         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9958         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9959       }
9960     }
9961   }
9962   PetscCall(PetscSectionSetUp(*cSec));
9963   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9964   PetscFunctionReturn(PETSC_SUCCESS);
9965 }
9966 
9967 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9968 {
9969   PetscSection    aSec;
9970   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9971   const PetscInt *anchors;
9972   PetscInt        numFields, f;
9973   IS              aIS;
9974   MatType         mtype;
9975   PetscBool       iscuda, iskokkos;
9976 
9977   PetscFunctionBegin;
9978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9979   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9980   PetscCall(PetscSectionGetStorageSize(section, &n));
9981   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9982   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9983   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9984   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9985   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9986   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9987   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9988   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9989   else mtype = MATSEQAIJ;
9990   PetscCall(MatSetType(*cMat, mtype));
9991   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9992   PetscCall(ISGetIndices(aIS, &anchors));
9993   /* cSec will be a subset of aSec and section */
9994   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9995   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9996   PetscCall(PetscMalloc1(m + 1, &i));
9997   i[0] = 0;
9998   PetscCall(PetscSectionGetNumFields(section, &numFields));
9999   for (p = pStart; p < pEnd; p++) {
10000     PetscInt rDof, rOff, r;
10001 
10002     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10003     if (!rDof) continue;
10004     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10005     if (numFields) {
10006       for (f = 0; f < numFields; f++) {
10007         annz = 0;
10008         for (r = 0; r < rDof; r++) {
10009           a = anchors[rOff + r];
10010           if (a < sStart || a >= sEnd) continue;
10011           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10012           annz += aDof;
10013         }
10014         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10015         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10016         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10017       }
10018     } else {
10019       annz = 0;
10020       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10021       for (q = 0; q < dof; q++) {
10022         a = anchors[rOff + q];
10023         if (a < sStart || a >= sEnd) continue;
10024         PetscCall(PetscSectionGetDof(section, a, &aDof));
10025         annz += aDof;
10026       }
10027       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10028       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10029       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10030     }
10031   }
10032   nnz = i[m];
10033   PetscCall(PetscMalloc1(nnz, &j));
10034   offset = 0;
10035   for (p = pStart; p < pEnd; p++) {
10036     if (numFields) {
10037       for (f = 0; f < numFields; f++) {
10038         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10039         for (q = 0; q < dof; q++) {
10040           PetscInt rDof, rOff, r;
10041           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10042           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10043           for (r = 0; r < rDof; r++) {
10044             PetscInt s;
10045 
10046             a = anchors[rOff + r];
10047             if (a < sStart || a >= sEnd) continue;
10048             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10049             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10050             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10051           }
10052         }
10053       }
10054     } else {
10055       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10056       for (q = 0; q < dof; q++) {
10057         PetscInt rDof, rOff, r;
10058         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10059         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10060         for (r = 0; r < rDof; r++) {
10061           PetscInt s;
10062 
10063           a = anchors[rOff + r];
10064           if (a < sStart || a >= sEnd) continue;
10065           PetscCall(PetscSectionGetDof(section, a, &aDof));
10066           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10067           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10068         }
10069       }
10070     }
10071   }
10072   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10073   PetscCall(PetscFree(i));
10074   PetscCall(PetscFree(j));
10075   PetscCall(ISRestoreIndices(aIS, &anchors));
10076   PetscFunctionReturn(PETSC_SUCCESS);
10077 }
10078 
10079 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10080 {
10081   DM_Plex     *plex = (DM_Plex *)dm->data;
10082   PetscSection anchorSection, section, cSec;
10083   Mat          cMat;
10084 
10085   PetscFunctionBegin;
10086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10087   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10088   if (anchorSection) {
10089     PetscInt Nf;
10090 
10091     PetscCall(DMGetLocalSection(dm, &section));
10092     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10093     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10094     PetscCall(DMGetNumFields(dm, &Nf));
10095     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10096     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10097     PetscCall(PetscSectionDestroy(&cSec));
10098     PetscCall(MatDestroy(&cMat));
10099   }
10100   PetscFunctionReturn(PETSC_SUCCESS);
10101 }
10102 
10103 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10104 {
10105   IS           subis;
10106   PetscSection section, subsection;
10107 
10108   PetscFunctionBegin;
10109   PetscCall(DMGetLocalSection(dm, &section));
10110   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10111   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10112   /* Create subdomain */
10113   PetscCall(DMPlexFilter(dm, label, value, subdm));
10114   /* Create submodel */
10115   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10116   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10117   PetscCall(DMSetLocalSection(*subdm, subsection));
10118   PetscCall(PetscSectionDestroy(&subsection));
10119   PetscCall(DMCopyDisc(dm, *subdm));
10120   /* Create map from submodel to global model */
10121   if (is) {
10122     PetscSection    sectionGlobal, subsectionGlobal;
10123     IS              spIS;
10124     const PetscInt *spmap;
10125     PetscInt       *subIndices;
10126     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10127     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10128 
10129     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10130     PetscCall(ISGetIndices(spIS, &spmap));
10131     PetscCall(PetscSectionGetNumFields(section, &Nf));
10132     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10133     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10134     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10135     for (p = pStart; p < pEnd; ++p) {
10136       PetscInt gdof, pSubSize = 0;
10137 
10138       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10139       if (gdof > 0) {
10140         for (f = 0; f < Nf; ++f) {
10141           PetscInt fdof, fcdof;
10142 
10143           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10144           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10145           pSubSize += fdof - fcdof;
10146         }
10147         subSize += pSubSize;
10148         if (pSubSize) {
10149           if (bs < 0) {
10150             bs = pSubSize;
10151           } else if (bs != pSubSize) {
10152             /* Layout does not admit a pointwise block size */
10153             bs = 1;
10154           }
10155         }
10156       }
10157     }
10158     /* Must have same blocksize on all procs (some might have no points) */
10159     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10160     bsLocal[1] = bs;
10161     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10162     if (bsMinMax[0] != bsMinMax[1]) {
10163       bs = 1;
10164     } else {
10165       bs = bsMinMax[0];
10166     }
10167     PetscCall(PetscMalloc1(subSize, &subIndices));
10168     for (p = pStart; p < pEnd; ++p) {
10169       PetscInt gdof, goff;
10170 
10171       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10172       if (gdof > 0) {
10173         const PetscInt point = spmap[p];
10174 
10175         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10176         for (f = 0; f < Nf; ++f) {
10177           PetscInt fdof, fcdof, fc, f2, poff = 0;
10178 
10179           /* Can get rid of this loop by storing field information in the global section */
10180           for (f2 = 0; f2 < f; ++f2) {
10181             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10182             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10183             poff += fdof - fcdof;
10184           }
10185           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10186           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10187           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10188         }
10189       }
10190     }
10191     PetscCall(ISRestoreIndices(spIS, &spmap));
10192     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10193     if (bs > 1) {
10194       /* We need to check that the block size does not come from non-contiguous fields */
10195       PetscInt i, j, set = 1;
10196       for (i = 0; i < subSize; i += bs) {
10197         for (j = 0; j < bs; ++j) {
10198           if (subIndices[i + j] != subIndices[i] + j) {
10199             set = 0;
10200             break;
10201           }
10202         }
10203       }
10204       if (set) PetscCall(ISSetBlockSize(*is, bs));
10205     }
10206     /* Attach nullspace */
10207     for (f = 0; f < Nf; ++f) {
10208       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10209       if ((*subdm)->nullspaceConstructors[f]) break;
10210     }
10211     if (f < Nf) {
10212       MatNullSpace nullSpace;
10213       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10214 
10215       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10216       PetscCall(MatNullSpaceDestroy(&nullSpace));
10217     }
10218   }
10219   PetscFunctionReturn(PETSC_SUCCESS);
10220 }
10221 
10222 /*@
10223   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10224 
10225   Input Parameters:
10226 + dm    - The `DM`
10227 - dummy - unused argument
10228 
10229   Options Database Key:
10230 . -dm_plex_monitor_throughput - Activate the monitor
10231 
10232   Level: developer
10233 
10234 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10235 @*/
10236 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10237 {
10238   PetscLogHandler default_handler;
10239 
10240   PetscFunctionBegin;
10241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10242   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10243   if (default_handler) {
10244     PetscLogEvent      event;
10245     PetscEventPerfInfo eventInfo;
10246     PetscReal          cellRate, flopRate;
10247     PetscInt           cStart, cEnd, Nf, N;
10248     const char        *name;
10249 
10250     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10251     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10252     PetscCall(DMGetNumFields(dm, &Nf));
10253     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10254     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10255     N        = (cEnd - cStart) * Nf * eventInfo.count;
10256     flopRate = eventInfo.flops / eventInfo.time;
10257     cellRate = N / eventInfo.time;
10258     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)));
10259   } else {
10260     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.");
10261   }
10262   PetscFunctionReturn(PETSC_SUCCESS);
10263 }
10264