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