xref: /petsc/src/dm/impls/plex/plex.c (revision a45b0079a386373b51c204163a3fd7088a7e52b3)
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 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 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;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
89 
90   PetscFunctionBegin;
91   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
93   PetscCall(ISGetLocalSize(valueIS, &Nct));
94   PetscCall(ISGetIndices(valueIS, &ctypes));
95   if (!Nct) cS = cE = 0;
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS = PetscMin(cS, ctS);
112     cE = PetscMax(cE, ctE);
113   }
114   PetscCall(ISDestroy(&valueIS));
115   // Reset label for fast lookup
116   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
117   if (cStart) *cStart = cS;
118   if (cEnd) *cEnd = cE;
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
123 {
124   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
125   PetscInt                *sStart, *sEnd;
126   PetscViewerVTKFieldType *ft;
127   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
128   DMLabel                  depthLabel, ctLabel;
129 
130   PetscFunctionBegin;
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161   }
162 
163   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
164   *types = 0;
165 
166   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
167     if (globalvcdof[c]) ++(*types);
168   }
169 
170   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
171   t = 0;
172   if (globalvcdof[DM_NUM_POLYTOPES]) {
173     sStart[t] = vStart;
174     sEnd[t]   = vEnd;
175     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
176     ++t;
177   }
178 
179   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
180     if (globalvcdof[c]) {
181       const DMPolytopeType ict = (DMPolytopeType)c;
182 
183       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
184       sStart[t] = cStart;
185       sEnd[t]   = cEnd;
186       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
187       ++t;
188     }
189   }
190 
191   if (!*types) {
192     if (field >= 0) {
193       const char *fieldname;
194 
195       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
196       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
197     } else {
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
199     }
200   }
201 
202   *ssStart = sStart;
203   *ssEnd   = sEnd;
204   *sft     = ft;
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
209 {
210   PetscFunctionBegin;
211   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
216 {
217   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
218   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
219 
220   PetscFunctionBegin;
221   *ft = PETSC_VTK_INVALID;
222   PetscCall(DMGetCoordinateDim(dm, &cdim));
223   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
224   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
225   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
226   if (field >= 0) {
227     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
228     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
229   } else {
230     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
231     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
232   }
233   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
234   if (globalvcdof[0]) {
235     *sStart = vStart;
236     *sEnd   = vEnd;
237     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
238     else *ft = PETSC_VTK_POINT_FIELD;
239   } else if (globalvcdof[1]) {
240     *sStart = cStart;
241     *sEnd   = cEnd;
242     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
243     else *ft = PETSC_VTK_CELL_FIELD;
244   } else {
245     if (field >= 0) {
246       const char *fieldname;
247 
248       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
249       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
250     } else {
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
252     }
253   }
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 /*@
258   DMPlexVecView1D - Plot many 1D solutions on the same line graph
259 
260   Collective
261 
262   Input Parameters:
263 + dm     - The `DMPLEX` object
264 . n      - The number of vectors
265 . u      - The array of local vectors
266 - viewer - The `PetscViewer`
267 
268   Level: advanced
269 
270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
271 @*/
272 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
273 {
274   PetscDS            ds;
275   PetscDraw          draw = NULL;
276   PetscDrawLG        lg;
277   Vec                coordinates;
278   const PetscScalar *coords, **sol;
279   PetscReal         *vals;
280   PetscInt          *Nc;
281   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
282   char             **names;
283 
284   PetscFunctionBegin;
285   PetscCall(DMGetDS(dm, &ds));
286   PetscCall(PetscDSGetNumFields(ds, &Nf));
287   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
288   PetscCall(PetscDSGetComponents(ds, &Nc));
289 
290   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
291   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
292   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
293 
294   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
295   for (i = 0, l = 0; i < n; ++i) {
296     const char *vname;
297 
298     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
299     for (f = 0; f < Nf; ++f) {
300       PetscObject disc;
301       const char *fname;
302       char        tmpname[PETSC_MAX_PATH_LEN];
303 
304       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
305       /* TODO Create names for components */
306       for (c = 0; c < Nc[f]; ++c, ++l) {
307         PetscCall(PetscObjectGetName(disc, &fname));
308         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
311         PetscCall(PetscStrallocpy(tmpname, &names[l]));
312       }
313     }
314   }
315   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
316   /* Just add P_1 support for now */
317   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
318   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
319   PetscCall(VecGetArrayRead(coordinates, &coords));
320   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
321   for (v = vStart; v < vEnd; ++v) {
322     PetscScalar *x, *svals;
323 
324     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
325     for (i = 0; i < n; ++i) {
326       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
327       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
328     }
329     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
330   }
331   PetscCall(VecRestoreArrayRead(coordinates, &coords));
332   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
333   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
334   PetscCall(PetscFree3(sol, names, vals));
335 
336   PetscCall(PetscDrawLGDraw(lg));
337   PetscCall(PetscDrawLGDestroy(&lg));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
342 {
343   DM dm;
344 
345   PetscFunctionBegin;
346   PetscCall(VecGetDM(u, &dm));
347   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
352 {
353   DM                 dm;
354   PetscSection       s;
355   PetscDraw          draw, popup;
356   DM                 cdm;
357   PetscSection       coordSection;
358   Vec                coordinates;
359   const PetscScalar *array;
360   PetscReal          lbound[3], ubound[3];
361   PetscReal          vbound[2], time;
362   PetscBool          flg;
363   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
364   const char        *name;
365   char               title[PETSC_MAX_PATH_LEN];
366 
367   PetscFunctionBegin;
368   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
369   PetscCall(VecGetDM(v, &dm));
370   PetscCall(DMGetCoordinateDim(dm, &dim));
371   PetscCall(DMGetLocalSection(dm, &s));
372   PetscCall(PetscSectionGetNumFields(s, &Nf));
373   PetscCall(DMGetCoarsenLevel(dm, &level));
374   PetscCall(DMGetCoordinateDM(dm, &cdm));
375   PetscCall(DMGetLocalSection(cdm, &coordSection));
376   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
377   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
378   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
379 
380   PetscCall(PetscObjectGetName((PetscObject)v, &name));
381   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
382 
383   PetscCall(VecGetLocalSize(coordinates, &N));
384   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
385   PetscCall(PetscDrawClear(draw));
386 
387   /* Could implement something like DMDASelectFields() */
388   for (f = 0; f < Nf; ++f) {
389     DM          fdm = dm;
390     Vec         fv  = v;
391     IS          fis;
392     char        prefix[PETSC_MAX_PATH_LEN];
393     const char *fname;
394 
395     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
396     PetscCall(PetscSectionGetFieldName(s, f, &fname));
397 
398     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
399     else prefix[0] = '\0';
400     if (Nf > 1) {
401       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
402       PetscCall(VecGetSubVector(v, fis, &fv));
403       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
404       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
405     }
406     for (comp = 0; comp < Nc; ++comp, ++w) {
407       PetscInt nmax = 2;
408 
409       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
410       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
411       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
412       PetscCall(PetscDrawSetTitle(draw, title));
413 
414       /* TODO Get max and min only for this component */
415       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
416       if (!flg) {
417         PetscCall(VecMin(fv, NULL, &vbound[0]));
418         PetscCall(VecMax(fv, NULL, &vbound[1]));
419         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
420       }
421 
422       PetscCall(PetscDrawGetPopup(draw, &popup));
423       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
424       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
425       PetscCall(VecGetArrayRead(fv, &array));
426       for (c = cStart; c < cEnd; ++c) {
427         PetscScalar       *coords = NULL, *a = NULL;
428         const PetscScalar *coords_arr;
429         PetscBool          isDG;
430         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
431 
432         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
433         if (a) {
434           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
435           color[1] = color[2] = color[3] = color[0];
436         } else {
437           PetscScalar *vals = NULL;
438           PetscInt     numVals, va;
439 
440           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
441           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);
442           switch (numVals / Nc) {
443           case 3: /* P1 Triangle */
444           case 4: /* P1 Quadrangle */
445             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
446             break;
447           case 6: /* P2 Triangle */
448           case 8: /* P2 Quadrangle */
449             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
450             break;
451           default:
452             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
453           }
454           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
455         }
456         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
457         switch (numCoords) {
458         case 6:
459         case 12: /* Localized triangle */
460           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]));
461           break;
462         case 8:
463         case 16: /* Localized quadrilateral */
464           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]));
465           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]));
466           break;
467         default:
468           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
469         }
470         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
471       }
472       PetscCall(VecRestoreArrayRead(fv, &array));
473       PetscCall(PetscDrawFlush(draw));
474       PetscCall(PetscDrawPause(draw));
475       PetscCall(PetscDrawSave(draw));
476     }
477     if (Nf > 1) {
478       PetscCall(VecRestoreSubVector(v, fis, &fv));
479       PetscCall(ISDestroy(&fis));
480       PetscCall(DMDestroy(&fdm));
481     }
482   }
483   PetscFunctionReturn(PETSC_SUCCESS);
484 }
485 
486 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
487 {
488   DM        dm;
489   PetscDraw draw;
490   PetscInt  dim;
491   PetscBool isnull;
492 
493   PetscFunctionBegin;
494   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
495   PetscCall(PetscDrawIsNull(draw, &isnull));
496   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
497 
498   PetscCall(VecGetDM(v, &dm));
499   PetscCall(DMGetCoordinateDim(dm, &dim));
500   switch (dim) {
501   case 1:
502     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
503     break;
504   case 2:
505     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
506     break;
507   default:
508     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
509   }
510   PetscFunctionReturn(PETSC_SUCCESS);
511 }
512 
513 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
514 {
515   DM                      dm;
516   Vec                     locv;
517   const char             *name;
518   PetscSection            section;
519   PetscInt                pStart, pEnd;
520   PetscInt                numFields;
521   PetscViewerVTKFieldType ft;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
526   PetscCall(PetscObjectGetName((PetscObject)v, &name));
527   PetscCall(PetscObjectSetName((PetscObject)locv, name));
528   PetscCall(VecCopy(v, locv));
529   PetscCall(DMGetLocalSection(dm, &section));
530   PetscCall(PetscSectionGetNumFields(section, &numFields));
531   if (!numFields) {
532     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
533     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
534   } else {
535     PetscInt f;
536 
537     for (f = 0; f < numFields; f++) {
538       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
539       if (ft == PETSC_VTK_INVALID) continue;
540       PetscCall(PetscObjectReference((PetscObject)locv));
541       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
542     }
543     PetscCall(VecDestroy(&locv));
544   }
545   PetscFunctionReturn(PETSC_SUCCESS);
546 }
547 
548 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
549 {
550   DM        dm;
551   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(v, &dm));
555   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
561   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
562     PetscInt    i, numFields;
563     PetscObject fe;
564     PetscBool   fem  = PETSC_FALSE;
565     Vec         locv = v;
566     const char *name;
567     PetscInt    step;
568     PetscReal   time;
569 
570     PetscCall(DMGetNumFields(dm, &numFields));
571     for (i = 0; i < numFields; i++) {
572       PetscCall(DMGetField(dm, i, NULL, &fe));
573       if (fe->classid == PETSCFE_CLASSID) {
574         fem = PETSC_TRUE;
575         break;
576       }
577     }
578     if (fem) {
579       PetscObject isZero;
580 
581       PetscCall(DMGetLocalVector(dm, &locv));
582       PetscCall(PetscObjectGetName((PetscObject)v, &name));
583       PetscCall(PetscObjectSetName((PetscObject)locv, name));
584       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
585       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
586       PetscCall(VecCopy(v, locv));
587       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
588       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
589     }
590     if (isvtk) {
591       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
592     } else if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
595 #else
596       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598     } else if (isdraw) {
599       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
600     } else if (isglvis) {
601       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
602       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
603       PetscCall(VecView_GLVis(locv, viewer));
604     } else if (iscgns) {
605 #if defined(PETSC_HAVE_CGNS)
606       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
607 #else
608       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
609 #endif
610     }
611     if (fem) {
612       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
613       PetscCall(DMRestoreLocalVector(dm, &locv));
614     }
615   } else {
616     PetscBool isseq;
617 
618     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
619     if (isseq) PetscCall(VecView_Seq(v, viewer));
620     else PetscCall(VecView_MPI(v, viewer));
621   }
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
626 {
627   DM        dm;
628   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (isvtk || isdraw || isglvis || iscgns) {
640     Vec         locv;
641     PetscObject isZero;
642     const char *name;
643 
644     PetscCall(DMGetLocalVector(dm, &locv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)locv, name));
647     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
648     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
649     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
650     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
651     PetscCall(VecView_Plex_Local(locv, viewer));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
653     PetscCall(DMRestoreLocalVector(dm, &locv));
654   } else if (ishdf5) {
655 #if defined(PETSC_HAVE_HDF5)
656     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
657 #else
658     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
659 #endif
660   } else if (isexodusii) {
661 #if defined(PETSC_HAVE_EXODUSII)
662     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
665 #endif
666   } else {
667     PetscBool isseq;
668 
669     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
670     if (isseq) PetscCall(VecView_Seq(v, viewer));
671     else PetscCall(VecView_MPI(v, viewer));
672   }
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   MPI_Comm          comm;
680   PetscViewerFormat format;
681   Vec               v;
682   PetscBool         isvtk, ishdf5;
683 
684   PetscFunctionBegin;
685   PetscCall(VecGetDM(originalv, &dm));
686   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
687   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
691   if (format == PETSC_VIEWER_NATIVE) {
692     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
693     /* this need a better fix */
694     if (dm->useNatural) {
695       if (dm->sfNatural) {
696         const char *vecname;
697         PetscInt    n, nroots;
698 
699         PetscCall(VecGetLocalSize(originalv, &n));
700         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
701         if (n == nroots) {
702           PetscCall(DMPlexCreateNaturalVector(dm, &v));
703           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
704           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
705           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
706           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
707         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
708       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
709     } else v = originalv;
710   } else v = originalv;
711 
712   if (ishdf5) {
713 #if defined(PETSC_HAVE_HDF5)
714     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
715 #else
716     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
717 #endif
718   } else if (isvtk) {
719     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
720   } else {
721     PetscBool isseq;
722 
723     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
724     if (isseq) PetscCall(VecView_Seq(v, viewer));
725     else PetscCall(VecView_MPI(v, viewer));
726   }
727   if (v != originalv) PetscCall(VecDestroy(&v));
728   PetscFunctionReturn(PETSC_SUCCESS);
729 }
730 
731 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
732 {
733   DM        dm;
734   PetscBool ishdf5;
735 
736   PetscFunctionBegin;
737   PetscCall(VecGetDM(v, &dm));
738   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
739   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
740   if (ishdf5) {
741     DM          dmBC;
742     Vec         gv;
743     const char *name;
744 
745     PetscCall(DMGetOutputDM(dm, &dmBC));
746     PetscCall(DMGetGlobalVector(dmBC, &gv));
747     PetscCall(PetscObjectGetName((PetscObject)v, &name));
748     PetscCall(PetscObjectSetName((PetscObject)gv, name));
749     PetscCall(VecLoad_Default(gv, viewer));
750     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
753   } else PetscCall(VecLoad_Default(v, viewer));
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
758 {
759   DM        dm;
760   PetscBool ishdf5, isexodusii;
761 
762   PetscFunctionBegin;
763   PetscCall(VecGetDM(v, &dm));
764   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
767   if (ishdf5) {
768 #if defined(PETSC_HAVE_HDF5)
769     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
770 #else
771     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
772 #endif
773   } else if (isexodusii) {
774 #if defined(PETSC_HAVE_EXODUSII)
775     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
776 #else
777     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
778 #endif
779   } else PetscCall(VecLoad_Default(v, viewer));
780   PetscFunctionReturn(PETSC_SUCCESS);
781 }
782 
783 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
784 {
785   DM                dm;
786   PetscViewerFormat format;
787   PetscBool         ishdf5;
788 
789   PetscFunctionBegin;
790   PetscCall(VecGetDM(originalv, &dm));
791   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
792   PetscCall(PetscViewerGetFormat(viewer, &format));
793   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
794   if (format == PETSC_VIEWER_NATIVE) {
795     if (dm->useNatural) {
796       if (dm->sfNatural) {
797         if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799           Vec         v;
800           const char *vecname;
801 
802           PetscCall(DMPlexCreateNaturalVector(dm, &v));
803           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
804           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
805           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
806           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
807           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
808           PetscCall(VecDestroy(&v));
809 #else
810           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
811 #endif
812         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
813       }
814     } else PetscCall(VecLoad_Default(originalv, viewer));
815   }
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
820 {
821   PetscSection       coordSection;
822   Vec                coordinates;
823   DMLabel            depthLabel, celltypeLabel;
824   const char        *name[4];
825   const PetscScalar *a;
826   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
827 
828   PetscFunctionBegin;
829   PetscCall(DMGetDimension(dm, &dim));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCoordinateSection(dm, &coordSection));
832   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
833   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
834   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
835   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
836   PetscCall(VecGetArrayRead(coordinates, &a));
837   name[0]       = "vertex";
838   name[1]       = "edge";
839   name[dim - 1] = "face";
840   name[dim]     = "cell";
841   for (c = cStart; c < cEnd; ++c) {
842     PetscInt *closure = NULL;
843     PetscInt  closureSize, cl, ct;
844 
845     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
847     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
848     PetscCall(PetscViewerASCIIPushTab(viewer));
849     for (cl = 0; cl < closureSize * 2; cl += 2) {
850       PetscInt point = closure[cl], depth, dof, off, d, p;
851 
852       if ((point < pStart) || (point >= pEnd)) continue;
853       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
854       if (!dof) continue;
855       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
856       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
858       for (p = 0; p < dof / dim; ++p) {
859         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
860         for (d = 0; d < dim; ++d) {
861           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
862           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
863         }
864         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
865       }
866       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
867     }
868     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
869     PetscCall(PetscViewerASCIIPopTab(viewer));
870   }
871   PetscCall(VecRestoreArrayRead(coordinates, &a));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 typedef enum {
876   CS_CARTESIAN,
877   CS_POLAR,
878   CS_CYLINDRICAL,
879   CS_SPHERICAL
880 } CoordSystem;
881 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
882 
883 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
884 {
885   PetscInt i;
886 
887   PetscFunctionBegin;
888   if (dim > 3) {
889     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
890   } else {
891     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
892 
893     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
894     switch (cs) {
895     case CS_CARTESIAN:
896       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
897       break;
898     case CS_POLAR:
899       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
900       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
901       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
902       break;
903     case CS_CYLINDRICAL:
904       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
905       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
906       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
907       trcoords[2] = coords[2];
908       break;
909     case CS_SPHERICAL:
910       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
911       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
912       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
913       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
914       break;
915     }
916     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
917   }
918   PetscFunctionReturn(PETSC_SUCCESS);
919 }
920 
921 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
922 {
923   DM_Plex          *mesh = (DM_Plex *)dm->data;
924   DM                cdm, cdmCell;
925   PetscSection      coordSection, coordSectionCell;
926   Vec               coordinates, coordinatesCell;
927   PetscViewerFormat format;
928 
929   PetscFunctionBegin;
930   PetscCall(PetscViewerGetFormat(viewer, &format));
931   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
932     const char *name;
933     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
934     PetscInt    pStart, pEnd, p, numLabels, l;
935     PetscMPIInt rank, size;
936 
937     PetscCall(DMGetCoordinateDM(dm, &cdm));
938     PetscCall(DMGetCoordinateSection(dm, &coordSection));
939     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
940     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
941     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
942     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
943     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
944     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
945     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
946     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
947     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
948     PetscCall(DMGetDimension(dm, &dim));
949     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
950     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
951     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
952     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
953     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
954     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
955     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
956     for (p = pStart; p < pEnd; ++p) {
957       PetscInt dof, off, s;
958 
959       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
960       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
961       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
962     }
963     PetscCall(PetscViewerFlush(viewer));
964     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
965     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
966     for (p = pStart; p < pEnd; ++p) {
967       PetscInt dof, off, c;
968 
969       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
970       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
971       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]));
972     }
973     PetscCall(PetscViewerFlush(viewer));
974     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
975     if (coordSection && coordinates) {
976       CoordSystem        cs = CS_CARTESIAN;
977       const PetscScalar *array, *arrayCell = NULL;
978       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
979       PetscMPIInt        rank;
980       const char        *name;
981 
982       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
983       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
984       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
985       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
986       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
987       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
988       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
989       pStart = PetscMin(pvStart, pcStart);
990       pEnd   = PetscMax(pvEnd, pcEnd);
991       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
994       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
995 
996       PetscCall(VecGetArrayRead(coordinates, &array));
997       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
998       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
999       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1000       for (p = pStart; p < pEnd; ++p) {
1001         PetscInt dof, off;
1002 
1003         if (p >= pvStart && p < pvEnd) {
1004           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1005           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1006           if (dof) {
1007             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1008             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1010           }
1011         }
1012         if (cdmCell && p >= pcStart && p < pcEnd) {
1013           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1014           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1015           if (dof) {
1016             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1017             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1019           }
1020         }
1021       }
1022       PetscCall(PetscViewerFlush(viewer));
1023       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1024       PetscCall(VecRestoreArrayRead(coordinates, &array));
1025       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1026     }
1027     PetscCall(DMGetNumLabels(dm, &numLabels));
1028     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1029     for (l = 0; l < numLabels; ++l) {
1030       DMLabel     label;
1031       PetscBool   isdepth;
1032       const char *name;
1033 
1034       PetscCall(DMGetLabelName(dm, l, &name));
1035       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1036       if (isdepth) continue;
1037       PetscCall(DMGetLabel(dm, name, &label));
1038       PetscCall(DMLabelView(label, viewer));
1039     }
1040     if (size > 1) {
1041       PetscSF sf;
1042 
1043       PetscCall(DMGetPointSF(dm, &sf));
1044       PetscCall(PetscSFView(sf, viewer));
1045     }
1046     if (mesh->periodic.face_sfs)
1047       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     if (lflg) {
1115       DMLabel lbl;
1116 
1117       PetscCall(DMGetLabel(dm, lname, &lbl));
1118       if (lbl) {
1119         PetscInt val, defval;
1120 
1121         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1122         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1123         for (c = pStart; c < pEnd; c++) {
1124           PetscInt *closure = NULL;
1125           PetscInt  closureSize;
1126 
1127           PetscCall(DMLabelGetValue(lbl, c, &val));
1128           if (val == defval) continue;
1129 
1130           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1132           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1133         }
1134       }
1135     }
1136 
1137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1138     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1139     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1140     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1141 \\documentclass[tikz]{standalone}\n\n\
1142 \\usepackage{pgflibraryshapes}\n\
1143 \\usetikzlibrary{backgrounds}\n\
1144 \\usetikzlibrary{arrows}\n\
1145 \\begin{document}\n"));
1146     if (size > 1) {
1147       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1148       for (p = 0; p < size; ++p) {
1149         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1150         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1151       }
1152       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1153     }
1154     if (drawHasse) {
1155       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1156 
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1169     }
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1171 
1172     /* Plot vertices */
1173     PetscCall(VecGetArray(coordinates, &coords));
1174     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1175     for (v = vStart; v < vEnd; ++v) {
1176       PetscInt  off, dof, d;
1177       PetscBool isLabeled = PETSC_FALSE;
1178 
1179       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1180       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1181       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1182       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1183       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1184       for (d = 0; d < dof; ++d) {
1185         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1186         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1187       }
1188       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1189       if (dim == 3) {
1190         PetscReal tmp = tcoords[1];
1191         tcoords[1]    = tcoords[2];
1192         tcoords[2]    = -tmp;
1193       }
1194       for (d = 0; d < dof; ++d) {
1195         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1196         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1197       }
1198       if (drawHasse) color = colors[0 % numColors];
1199       else color = colors[rank % numColors];
1200       for (l = 0; l < numLabels; ++l) {
1201         PetscInt val;
1202         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1203         if (val >= 0) {
1204           color     = lcolors[l % numLColors];
1205           isLabeled = PETSC_TRUE;
1206           break;
1207         }
1208       }
1209       if (drawNumbers[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1211       } else if (drawColors[0]) {
1212         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1213       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1214     }
1215     PetscCall(VecRestoreArray(coordinates, &coords));
1216     PetscCall(PetscViewerFlush(viewer));
1217     /* Plot edges */
1218     if (plotEdges) {
1219       PetscCall(VecGetArray(coordinates, &coords));
1220       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1221       for (e = eStart; e < eEnd; ++e) {
1222         const PetscInt *cone;
1223         PetscInt        coneSize, offA, offB, dof, d;
1224 
1225         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1226         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1227         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1228         PetscCall(DMPlexGetCone(dm, e, &cone));
1229         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1231         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1232         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1233         for (d = 0; d < dof; ++d) {
1234           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1235           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1236         }
1237         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1238         if (dim == 3) {
1239           PetscReal tmp = tcoords[1];
1240           tcoords[1]    = tcoords[2];
1241           tcoords[2]    = -tmp;
1242         }
1243         for (d = 0; d < dof; ++d) {
1244           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1245           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1246         }
1247         if (drawHasse) color = colors[1 % numColors];
1248         else color = colors[rank % numColors];
1249         for (l = 0; l < numLabels; ++l) {
1250           PetscInt val;
1251           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1252           if (val >= 0) {
1253             color = lcolors[l % numLColors];
1254             break;
1255           }
1256         }
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1258       }
1259       PetscCall(VecRestoreArray(coordinates, &coords));
1260       PetscCall(PetscViewerFlush(viewer));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1262     }
1263     /* Plot cells */
1264     if (dim == 3 || !drawNumbers[1]) {
1265       for (e = eStart; e < eEnd; ++e) {
1266         const PetscInt *cone;
1267 
1268         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1269         color = colors[rank % numColors];
1270         for (l = 0; l < numLabels; ++l) {
1271           PetscInt val;
1272           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1273           if (val >= 0) {
1274             color = lcolors[l % numLColors];
1275             break;
1276           }
1277         }
1278         PetscCall(DMPlexGetCone(dm, e, &cone));
1279         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1280       }
1281     } else {
1282       DMPolytopeType ct;
1283 
1284       /* Drawing a 2D polygon */
1285       for (c = cStart; c < cEnd; ++c) {
1286         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1287         PetscCall(DMPlexGetCellType(dm, c, &ct));
1288         if (DMPolytopeTypeIsHybrid(ct)) {
1289           const PetscInt *cone;
1290           PetscInt        coneSize, e;
1291 
1292           PetscCall(DMPlexGetCone(dm, c, &cone));
1293           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1294           for (e = 0; e < coneSize; ++e) {
1295             const PetscInt *econe;
1296 
1297             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1298             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));
1299           }
1300         } else {
1301           PetscInt *closure = NULL;
1302           PetscInt  closureSize, Nv = 0, v;
1303 
1304           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1305           for (p = 0; p < closureSize * 2; p += 2) {
1306             const PetscInt point = closure[p];
1307 
1308             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1309           }
1310           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1311           for (v = 0; v <= Nv; ++v) {
1312             const PetscInt vertex = closure[v % Nv];
1313 
1314             if (v > 0) {
1315               if (plotEdges) {
1316                 const PetscInt *edge;
1317                 PetscInt        endpoints[2], ne;
1318 
1319                 endpoints[0] = closure[v - 1];
1320                 endpoints[1] = vertex;
1321                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1322                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1323                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1324                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1325               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1326             }
1327             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1328           }
1329           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1330           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331         }
1332       }
1333     }
1334     for (c = cStart; c < cEnd; ++c) {
1335       double             ccoords[3] = {0.0, 0.0, 0.0};
1336       PetscBool          isLabeled  = PETSC_FALSE;
1337       PetscScalar       *cellCoords = NULL;
1338       const PetscScalar *array;
1339       PetscInt           numCoords, cdim, d;
1340       PetscBool          isDG;
1341 
1342       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1343       PetscCall(DMGetCoordinateDim(dm, &cdim));
1344       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1345       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1346       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1347       for (p = 0; p < numCoords / cdim; ++p) {
1348         for (d = 0; d < cdim; ++d) {
1349           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1350           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1351         }
1352         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1353         if (cdim == 3) {
1354           PetscReal tmp = tcoords[1];
1355           tcoords[1]    = tcoords[2];
1356           tcoords[2]    = -tmp;
1357         }
1358         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1359       }
1360       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1361       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1362       for (d = 0; d < cdim; ++d) {
1363         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1364         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1365       }
1366       if (drawHasse) color = colors[depth % numColors];
1367       else color = colors[rank % numColors];
1368       for (l = 0; l < numLabels; ++l) {
1369         PetscInt val;
1370         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1371         if (val >= 0) {
1372           color     = lcolors[l % numLColors];
1373           isLabeled = PETSC_TRUE;
1374           break;
1375         }
1376       }
1377       if (drawNumbers[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1379       } else if (drawColors[dim]) {
1380         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1381       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1382     }
1383     if (drawHasse) {
1384       color = colors[depth % numColors];
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1389       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1390 
1391       color = colors[1 % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       color = colors[0 % numColors];
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1403       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1404 
1405       for (p = pStart; p < pEnd; ++p) {
1406         const PetscInt *cone;
1407         PetscInt        coneSize, cp;
1408 
1409         PetscCall(DMPlexGetCone(dm, p, &cone));
1410         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1411         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1412       }
1413     }
1414     PetscCall(PetscViewerFlush(viewer));
1415     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1417     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1418     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1419     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1420     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1421     PetscCall(PetscFree3(names, colors, lcolors));
1422     PetscCall(PetscBTDestroy(&wp));
1423   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1424     Vec                    cown, acown;
1425     VecScatter             sct;
1426     ISLocalToGlobalMapping g2l;
1427     IS                     gid, acis;
1428     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1429     MPI_Group              ggroup, ngroup;
1430     PetscScalar           *array, nid;
1431     const PetscInt        *idxs;
1432     PetscInt              *idxs2, *start, *adjacency, *work;
1433     PetscInt64             lm[3], gm[3];
1434     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1435     PetscMPIInt            d1, d2, rank;
1436 
1437     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1438     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1439 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1440     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1441 #endif
1442     if (ncomm != MPI_COMM_NULL) {
1443       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1444       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1445       d1 = 0;
1446       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1447       nid = d2;
1448       PetscCallMPI(MPI_Group_free(&ggroup));
1449       PetscCallMPI(MPI_Group_free(&ngroup));
1450       PetscCallMPI(MPI_Comm_free(&ncomm));
1451     } else nid = 0.0;
1452 
1453     /* Get connectivity */
1454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1455     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1456 
1457     /* filter overlapped local cells */
1458     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1459     PetscCall(ISGetIndices(gid, &idxs));
1460     PetscCall(ISGetLocalSize(gid, &cum));
1461     PetscCall(PetscMalloc1(cum, &idxs2));
1462     for (c = cStart, cum = 0; c < cEnd; c++) {
1463       if (idxs[c - cStart] < 0) continue;
1464       idxs2[cum++] = idxs[c - cStart];
1465     }
1466     PetscCall(ISRestoreIndices(gid, &idxs));
1467     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1468     PetscCall(ISDestroy(&gid));
1469     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1470 
1471     /* support for node-aware cell locality */
1472     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1473     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1474     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1475     PetscCall(VecGetArray(cown, &array));
1476     for (c = 0; c < numVertices; c++) array[c] = nid;
1477     PetscCall(VecRestoreArray(cown, &array));
1478     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1479     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1481     PetscCall(ISDestroy(&acis));
1482     PetscCall(VecScatterDestroy(&sct));
1483     PetscCall(VecDestroy(&cown));
1484 
1485     /* compute edgeCut */
1486     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1487     PetscCall(PetscMalloc1(cum, &work));
1488     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1489     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1490     PetscCall(ISDestroy(&gid));
1491     PetscCall(VecGetArray(acown, &array));
1492     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1493       PetscInt totl;
1494 
1495       totl = start[c + 1] - start[c];
1496       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1497       for (i = 0; i < totl; i++) {
1498         if (work[i] < 0) {
1499           ect += 1;
1500           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1501         }
1502       }
1503     }
1504     PetscCall(PetscFree(work));
1505     PetscCall(VecRestoreArray(acown, &array));
1506     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1507     lm[1] = -numVertices;
1508     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1509     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1510     lm[0] = ect;                     /* edgeCut */
1511     lm[1] = ectn;                    /* node-aware edgeCut */
1512     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1513     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1514     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1515 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1517 #else
1518     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1519 #endif
1520     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1521     PetscCall(PetscFree(start));
1522     PetscCall(PetscFree(adjacency));
1523     PetscCall(VecDestroy(&acown));
1524   } else {
1525     const char    *name;
1526     PetscInt      *sizes, *hybsizes, *ghostsizes;
1527     PetscInt       locDepth, depth, cellHeight, dim, d;
1528     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1529     PetscInt       numLabels, l, maxSize = 17;
1530     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1531     MPI_Comm       comm;
1532     PetscMPIInt    size, rank;
1533 
1534     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1535     PetscCallMPI(MPI_Comm_size(comm, &size));
1536     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1537     PetscCall(DMGetDimension(dm, &dim));
1538     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1539     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1540     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1541     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1542     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1543     PetscCall(DMPlexGetDepth(dm, &locDepth));
1544     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1545     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1546     gcNum = gcEnd - gcStart;
1547     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1548     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1549     for (d = 0; d <= depth; d++) {
1550       PetscInt Nc[2] = {0, 0}, ict;
1551 
1552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1553       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1554       ict = ct0;
1555       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1556       ct0 = (DMPolytopeType)ict;
1557       for (p = pStart; p < pEnd; ++p) {
1558         DMPolytopeType ct;
1559 
1560         PetscCall(DMPlexGetCellType(dm, p, &ct));
1561         if (ct == ct0) ++Nc[0];
1562         else ++Nc[1];
1563       }
1564       if (size < maxSize) {
1565         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1566         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1567         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1568         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1569         for (p = 0; p < size; ++p) {
1570           if (rank == 0) {
1571             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1572             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1573             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1574           }
1575         }
1576       } else {
1577         PetscInt locMinMax[2];
1578 
1579         locMinMax[0] = Nc[0] + Nc[1];
1580         locMinMax[1] = Nc[0] + Nc[1];
1581         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1582         locMinMax[0] = Nc[1];
1583         locMinMax[1] = Nc[1];
1584         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1585         if (d == depth) {
1586           locMinMax[0] = gcNum;
1587           locMinMax[1] = gcNum;
1588           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1589         }
1590         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1591         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1592         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1593         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1594       }
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1596     }
1597     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1598     {
1599       const PetscReal *maxCell;
1600       const PetscReal *L;
1601       PetscBool        localized;
1602 
1603       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1604       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1605       if (L || localized) {
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1607         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1608         if (L) {
1609           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1610           for (d = 0; d < dim; ++d) {
1611             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1612             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1613           }
1614           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1615         }
1616         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1617         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1618       }
1619     }
1620     PetscCall(DMGetNumLabels(dm, &numLabels));
1621     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1622     for (l = 0; l < numLabels; ++l) {
1623       DMLabel         label;
1624       const char     *name;
1625       IS              valueIS;
1626       const PetscInt *values;
1627       PetscInt        numValues, v;
1628 
1629       PetscCall(DMGetLabelName(dm, l, &name));
1630       PetscCall(DMGetLabel(dm, name, &label));
1631       PetscCall(DMLabelGetNumValues(label, &numValues));
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1633       PetscCall(DMLabelGetValueIS(label, &valueIS));
1634       PetscCall(ISGetIndices(valueIS, &values));
1635       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1636       for (v = 0; v < numValues; ++v) {
1637         PetscInt size;
1638 
1639         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1640         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1642       }
1643       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1644       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1645       PetscCall(ISRestoreIndices(valueIS, &values));
1646       PetscCall(ISDestroy(&valueIS));
1647     }
1648     {
1649       char    **labelNames;
1650       PetscInt  Nl = numLabels;
1651       PetscBool flg;
1652 
1653       PetscCall(PetscMalloc1(Nl, &labelNames));
1654       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1655       for (l = 0; l < Nl; ++l) {
1656         DMLabel label;
1657 
1658         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1659         if (flg) {
1660           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1661           PetscCall(DMLabelView(label, viewer));
1662         }
1663         PetscCall(PetscFree(labelNames[l]));
1664       }
1665       PetscCall(PetscFree(labelNames));
1666     }
1667     /* If no fields are specified, people do not want to see adjacency */
1668     if (dm->Nf) {
1669       PetscInt f;
1670 
1671       for (f = 0; f < dm->Nf; ++f) {
1672         const char *name;
1673 
1674         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1675         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1676         PetscCall(PetscViewerASCIIPushTab(viewer));
1677         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1678         if (dm->fields[f].adjacency[0]) {
1679           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1680           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1681         } else {
1682           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1683           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1684         }
1685         PetscCall(PetscViewerASCIIPopTab(viewer));
1686       }
1687     }
1688     PetscCall(DMGetCoarseDM(dm, &cdm));
1689     if (cdm) {
1690       PetscCall(PetscViewerASCIIPushTab(viewer));
1691       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1692       PetscCall(DMPlexView_Ascii(cdm, viewer));
1693       PetscCall(PetscViewerASCIIPopTab(viewer));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1700 {
1701   DMPolytopeType ct;
1702   PetscMPIInt    rank;
1703   PetscInt       cdim;
1704 
1705   PetscFunctionBegin;
1706   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1707   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1708   PetscCall(DMGetCoordinateDim(dm, &cdim));
1709   switch (ct) {
1710   case DM_POLYTOPE_SEGMENT:
1711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1712     switch (cdim) {
1713     case 1: {
1714       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1715       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1716 
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1719       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1720     } break;
1721     case 2: {
1722       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1723       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1724       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1725 
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1727       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));
1728       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));
1729     } break;
1730     default:
1731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1732     }
1733     break;
1734   case DM_POLYTOPE_TRIANGLE:
1735     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));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1738     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1739     break;
1740   case DM_POLYTOPE_QUADRILATERAL:
1741     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));
1742     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));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1746     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1747     break;
1748   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1749     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));
1750     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));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_FV_GHOST:
1757     break;
1758   default:
1759     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1760   }
1761   PetscFunctionReturn(PETSC_SUCCESS);
1762 }
1763 
1764 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1765 {
1766   PetscReal   centroid[2] = {0., 0.};
1767   PetscMPIInt rank;
1768   PetscInt    fillColor;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1773   for (PetscInt v = 0; v < Nv; ++v) {
1774     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1775     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1776   }
1777   for (PetscInt e = 0; e < Nv; ++e) {
1778     refCoords[0] = refVertices[e * 2 + 0];
1779     refCoords[1] = refVertices[e * 2 + 1];
1780     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1781       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1782       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1783     }
1784     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1785     for (PetscInt d = 0; d < edgeDiv; ++d) {
1786       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));
1787       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1788     }
1789   }
1790   PetscFunctionReturn(PETSC_SUCCESS);
1791 }
1792 
1793 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1794 {
1795   DMPolytopeType ct;
1796 
1797   PetscFunctionBegin;
1798   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1799   switch (ct) {
1800   case DM_POLYTOPE_TRIANGLE: {
1801     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1802 
1803     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1804   } break;
1805   case DM_POLYTOPE_QUADRILATERAL: {
1806     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1807 
1808     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1809   } break;
1810   default:
1811     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1812   }
1813   PetscFunctionReturn(PETSC_SUCCESS);
1814 }
1815 
1816 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1817 {
1818   PetscDraw    draw;
1819   DM           cdm;
1820   PetscSection coordSection;
1821   Vec          coordinates;
1822   PetscReal    xyl[3], xyr[3];
1823   PetscReal   *refCoords, *edgeCoords;
1824   PetscBool    isnull, drawAffine;
1825   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1826 
1827   PetscFunctionBegin;
1828   PetscCall(DMGetCoordinateDim(dm, &dim));
1829   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1830   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1831   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1832   edgeDiv    = cDegree + 1;
1833   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1834   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1835   PetscCall(DMGetCoordinateDM(dm, &cdm));
1836   PetscCall(DMGetLocalSection(cdm, &coordSection));
1837   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1838   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1839   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1840 
1841   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1842   PetscCall(PetscDrawIsNull(draw, &isnull));
1843   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1844   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1845 
1846   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1847   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1848   PetscCall(PetscDrawClear(draw));
1849 
1850   for (c = cStart; c < cEnd; ++c) {
1851     PetscScalar       *coords = NULL;
1852     const PetscScalar *coords_arr;
1853     PetscInt           numCoords;
1854     PetscBool          isDG;
1855 
1856     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1857     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1858     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1859     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1860   }
1861   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1862   PetscCall(PetscDrawFlush(draw));
1863   PetscCall(PetscDrawPause(draw));
1864   PetscCall(PetscDrawSave(draw));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1869 {
1870   DM           odm = dm, rdm = dm, cdm;
1871   PetscFE      fe;
1872   PetscSpace   sp;
1873   PetscClassId id;
1874   PetscInt     degree;
1875   PetscBool    hoView = PETSC_TRUE;
1876 
1877   PetscFunctionBegin;
1878   PetscObjectOptionsBegin((PetscObject)dm);
1879   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1880   PetscOptionsEnd();
1881   PetscCall(PetscObjectReference((PetscObject)dm));
1882   *hdm = dm;
1883   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1884   PetscCall(DMGetCoordinateDM(dm, &cdm));
1885   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1886   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1887   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1888   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1889   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1890   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1891     DM  cdm, rcdm;
1892     Mat In;
1893     Vec cl, rcl;
1894 
1895     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1896     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1897     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1898     PetscCall(DMGetCoordinateDM(odm, &cdm));
1899     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1900     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1901     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1902     PetscCall(DMSetCoarseDM(rcdm, cdm));
1903     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1904     PetscCall(MatMult(In, cl, rcl));
1905     PetscCall(MatDestroy(&In));
1906     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1907     PetscCall(DMDestroy(&odm));
1908     odm = rdm;
1909   }
1910   *hdm = rdm;
1911   PetscFunctionReturn(PETSC_SUCCESS);
1912 }
1913 
1914 #if defined(PETSC_HAVE_EXODUSII)
1915   #include <exodusII.h>
1916   #include <petscviewerexodusii.h>
1917 #endif
1918 
1919 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1920 {
1921   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1922   char      name[PETSC_MAX_PATH_LEN];
1923 
1924   PetscFunctionBegin;
1925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1926   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1932   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1933   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1934   if (iascii) {
1935     PetscViewerFormat format;
1936     PetscCall(PetscViewerGetFormat(viewer, &format));
1937     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1938     else PetscCall(DMPlexView_Ascii(dm, viewer));
1939   } else if (ishdf5) {
1940 #if defined(PETSC_HAVE_HDF5)
1941     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1942 #else
1943     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1944 #endif
1945   } else if (isvtk) {
1946     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1947   } else if (isdraw) {
1948     DM hdm;
1949 
1950     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1951     PetscCall(DMPlexView_Draw(hdm, viewer));
1952     PetscCall(DMDestroy(&hdm));
1953   } else if (isglvis) {
1954     PetscCall(DMPlexView_GLVis(dm, viewer));
1955 #if defined(PETSC_HAVE_EXODUSII)
1956   } else if (isexodus) {
1957     /*
1958       exodusII requires that all sets be part of exactly one cell set.
1959       If the dm does not have a "Cell Sets" label defined, we create one
1960       with ID 1, containing all cells.
1961       Note that if the Cell Sets label is defined but does not cover all cells,
1962       we may still have a problem. This should probably be checked here or in the viewer;
1963     */
1964     PetscInt numCS;
1965     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1966     if (!numCS) {
1967       PetscInt cStart, cEnd, c;
1968       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1969       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1970       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1971     }
1972     PetscCall(DMView_PlexExodusII(dm, viewer));
1973 #endif
1974 #if defined(PETSC_HAVE_CGNS)
1975   } else if (iscgns) {
1976     PetscCall(DMView_PlexCGNS(dm, viewer));
1977 #endif
1978   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1979   /* Optionally view the partition */
1980   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1981   if (flg) {
1982     Vec ranks;
1983     PetscCall(DMPlexCreateRankField(dm, &ranks));
1984     PetscCall(VecView(ranks, viewer));
1985     PetscCall(VecDestroy(&ranks));
1986   }
1987   /* Optionally view a label */
1988   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1989   if (flg) {
1990     DMLabel label;
1991     Vec     val;
1992 
1993     PetscCall(DMGetLabel(dm, name, &label));
1994     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1995     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1996     PetscCall(VecView(val, viewer));
1997     PetscCall(VecDestroy(&val));
1998   }
1999   PetscFunctionReturn(PETSC_SUCCESS);
2000 }
2001 
2002 /*@
2003   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2004 
2005   Collective
2006 
2007   Input Parameters:
2008 + dm     - The `DM` whose topology is to be saved
2009 - viewer - The `PetscViewer` to save it in
2010 
2011   Level: advanced
2012 
2013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2014 @*/
2015 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2016 {
2017   PetscBool ishdf5;
2018 
2019   PetscFunctionBegin;
2020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2021   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2022   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2023   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2024   if (ishdf5) {
2025 #if defined(PETSC_HAVE_HDF5)
2026     PetscViewerFormat format;
2027     PetscCall(PetscViewerGetFormat(viewer, &format));
2028     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2029       IS globalPointNumbering;
2030 
2031       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2032       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2033       PetscCall(ISDestroy(&globalPointNumbering));
2034     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2035 #else
2036     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2037 #endif
2038   }
2039   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2040   PetscFunctionReturn(PETSC_SUCCESS);
2041 }
2042 
2043 /*@
2044   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2045 
2046   Collective
2047 
2048   Input Parameters:
2049 + dm     - The `DM` whose coordinates are to be saved
2050 - viewer - The `PetscViewer` for saving
2051 
2052   Level: advanced
2053 
2054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2055 @*/
2056 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2057 {
2058   PetscBool ishdf5;
2059 
2060   PetscFunctionBegin;
2061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2062   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2064   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2065   if (ishdf5) {
2066 #if defined(PETSC_HAVE_HDF5)
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2070       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2071     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   }
2076   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2077   PetscFunctionReturn(PETSC_SUCCESS);
2078 }
2079 
2080 /*@
2081   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2082 
2083   Collective
2084 
2085   Input Parameters:
2086 + dm     - The `DM` whose labels are to be saved
2087 - viewer - The `PetscViewer` for saving
2088 
2089   Level: advanced
2090 
2091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2092 @*/
2093 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2094 {
2095   PetscBool ishdf5;
2096 
2097   PetscFunctionBegin;
2098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2099   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2100   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2101   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2102   if (ishdf5) {
2103 #if defined(PETSC_HAVE_HDF5)
2104     IS                globalPointNumbering;
2105     PetscViewerFormat format;
2106 
2107     PetscCall(PetscViewerGetFormat(viewer, &format));
2108     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2109       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2110       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2111       PetscCall(ISDestroy(&globalPointNumbering));
2112     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2113 #else
2114     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2115 #endif
2116   }
2117   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2118   PetscFunctionReturn(PETSC_SUCCESS);
2119 }
2120 
2121 /*@
2122   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2123 
2124   Collective
2125 
2126   Input Parameters:
2127 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2128 . viewer    - The `PetscViewer` for saving
2129 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2130 
2131   Level: advanced
2132 
2133   Notes:
2134   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.
2135 
2136   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 (or in case `sectiondm` is `NULL`) 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.
2137 
2138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2139 @*/
2140 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2141 {
2142   PetscBool ishdf5;
2143 
2144   PetscFunctionBegin;
2145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2146   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2147   if (!sectiondm) sectiondm = dm;
2148   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2149   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2150   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2151   if (ishdf5) {
2152 #if defined(PETSC_HAVE_HDF5)
2153     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2154 #else
2155     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2156 #endif
2157   }
2158   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2159   PetscFunctionReturn(PETSC_SUCCESS);
2160 }
2161 
2162 /*@
2163   DMPlexGlobalVectorView - Saves a global vector
2164 
2165   Collective
2166 
2167   Input Parameters:
2168 + dm        - The `DM` that represents the topology
2169 . viewer    - The `PetscViewer` to save data with
2170 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2171 - vec       - The global vector to be saved
2172 
2173   Level: advanced
2174 
2175   Notes:
2176   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 (or in case `sectiondm` is `NULL`) 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.
2177 
2178   Calling sequence:
2179 .vb
2180        DMCreate(PETSC_COMM_WORLD, &dm);
2181        DMSetType(dm, DMPLEX);
2182        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2183        DMClone(dm, &sectiondm);
2184        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2185        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2186        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2187        PetscSectionSetChart(section, pStart, pEnd);
2188        PetscSectionSetUp(section);
2189        DMSetLocalSection(sectiondm, section);
2190        PetscSectionDestroy(&section);
2191        DMGetGlobalVector(sectiondm, &vec);
2192        PetscObjectSetName((PetscObject)vec, "vec_name");
2193        DMPlexTopologyView(dm, viewer);
2194        DMPlexSectionView(dm, viewer, sectiondm);
2195        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2196        DMRestoreGlobalVector(sectiondm, &vec);
2197        DMDestroy(&sectiondm);
2198        DMDestroy(&dm);
2199 .ve
2200 
2201 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2202 @*/
2203 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2204 {
2205   PetscBool ishdf5;
2206 
2207   PetscFunctionBegin;
2208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2209   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2210   if (!sectiondm) sectiondm = dm;
2211   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2212   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2213   /* Check consistency */
2214   {
2215     PetscSection section;
2216     PetscBool    includesConstraints;
2217     PetscInt     m, m1;
2218 
2219     PetscCall(VecGetLocalSize(vec, &m1));
2220     PetscCall(DMGetGlobalSection(sectiondm, &section));
2221     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2222     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2223     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2224     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2225   }
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2231 #else
2232     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2233 #endif
2234   }
2235   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2236   PetscFunctionReturn(PETSC_SUCCESS);
2237 }
2238 
2239 /*@
2240   DMPlexLocalVectorView - Saves a local vector
2241 
2242   Collective
2243 
2244   Input Parameters:
2245 + dm        - The `DM` that represents the topology
2246 . viewer    - The `PetscViewer` to save data with
2247 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2248 - vec       - The local vector to be saved
2249 
2250   Level: advanced
2251 
2252   Note:
2253   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 (or in case `sectiondm` is `NULL`) 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.
2254 
2255   Calling sequence:
2256 .vb
2257        DMCreate(PETSC_COMM_WORLD, &dm);
2258        DMSetType(dm, DMPLEX);
2259        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2260        DMClone(dm, &sectiondm);
2261        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2262        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2263        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2264        PetscSectionSetChart(section, pStart, pEnd);
2265        PetscSectionSetUp(section);
2266        DMSetLocalSection(sectiondm, section);
2267        DMGetLocalVector(sectiondm, &vec);
2268        PetscObjectSetName((PetscObject)vec, "vec_name");
2269        DMPlexTopologyView(dm, viewer);
2270        DMPlexSectionView(dm, viewer, sectiondm);
2271        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2272        DMRestoreLocalVector(sectiondm, &vec);
2273        DMDestroy(&sectiondm);
2274        DMDestroy(&dm);
2275 .ve
2276 
2277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2278 @*/
2279 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2280 {
2281   PetscBool ishdf5;
2282 
2283   PetscFunctionBegin;
2284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2285   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2286   if (!sectiondm) sectiondm = dm;
2287   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2288   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2289   /* Check consistency */
2290   {
2291     PetscSection section;
2292     PetscBool    includesConstraints;
2293     PetscInt     m, m1;
2294 
2295     PetscCall(VecGetLocalSize(vec, &m1));
2296     PetscCall(DMGetLocalSection(sectiondm, &section));
2297     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2298     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2299     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2300     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2301   }
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2304   if (ishdf5) {
2305 #if defined(PETSC_HAVE_HDF5)
2306     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2307 #else
2308     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2309 #endif
2310   }
2311   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2312   PetscFunctionReturn(PETSC_SUCCESS);
2313 }
2314 
2315 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2316 {
2317   PetscBool ishdf5;
2318 
2319   PetscFunctionBegin;
2320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2321   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2322   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2323   if (ishdf5) {
2324 #if defined(PETSC_HAVE_HDF5)
2325     PetscViewerFormat format;
2326     PetscCall(PetscViewerGetFormat(viewer, &format));
2327     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2328       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2329     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2330       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2331     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2332     PetscFunctionReturn(PETSC_SUCCESS);
2333 #else
2334     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2335 #endif
2336   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2337 }
2338 
2339 /*@
2340   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2341 
2342   Collective
2343 
2344   Input Parameters:
2345 + dm     - The `DM` into which the topology is loaded
2346 - viewer - The `PetscViewer` for the saved topology
2347 
2348   Output Parameter:
2349 . 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
2350 
2351   Level: advanced
2352 
2353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2354           `PetscViewer`, `PetscSF`
2355 @*/
2356 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2357 {
2358   PetscBool ishdf5;
2359 
2360   PetscFunctionBegin;
2361   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2362   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2363   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2364   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2365   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2366   if (ishdf5) {
2367 #if defined(PETSC_HAVE_HDF5)
2368     PetscViewerFormat format;
2369     PetscCall(PetscViewerGetFormat(viewer, &format));
2370     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2371       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2372     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2373 #else
2374     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2375 #endif
2376   }
2377   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2378   PetscFunctionReturn(PETSC_SUCCESS);
2379 }
2380 
2381 /*@
2382   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2383 
2384   Collective
2385 
2386   Input Parameters:
2387 + dm                   - The `DM` into which the coordinates are loaded
2388 . viewer               - The `PetscViewer` for the saved coordinates
2389 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2390 
2391   Level: advanced
2392 
2393 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2394           `PetscSF`, `PetscViewer`
2395 @*/
2396 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2397 {
2398   PetscBool ishdf5;
2399 
2400   PetscFunctionBegin;
2401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2402   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2403   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2404   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2405   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2406   if (ishdf5) {
2407 #if defined(PETSC_HAVE_HDF5)
2408     PetscViewerFormat format;
2409     PetscCall(PetscViewerGetFormat(viewer, &format));
2410     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2411       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2412     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2413 #else
2414     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2415 #endif
2416   }
2417   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2418   PetscFunctionReturn(PETSC_SUCCESS);
2419 }
2420 
2421 /*@
2422   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2423 
2424   Collective
2425 
2426   Input Parameters:
2427 + dm                   - The `DM` into which the labels are loaded
2428 . viewer               - The `PetscViewer` for the saved labels
2429 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2430 
2431   Level: advanced
2432 
2433   Note:
2434   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2435 
2436 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2437           `PetscSF`, `PetscViewer`
2438 @*/
2439 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2440 {
2441   PetscBool ishdf5;
2442 
2443   PetscFunctionBegin;
2444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2445   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2446   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2447   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2448   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2449   if (ishdf5) {
2450 #if defined(PETSC_HAVE_HDF5)
2451     PetscViewerFormat format;
2452 
2453     PetscCall(PetscViewerGetFormat(viewer, &format));
2454     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2455       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2456     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2457 #else
2458     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2459 #endif
2460   }
2461   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2462   PetscFunctionReturn(PETSC_SUCCESS);
2463 }
2464 
2465 /*@
2466   DMPlexSectionLoad - Loads section into a `DMPLEX`
2467 
2468   Collective
2469 
2470   Input Parameters:
2471 + dm                   - The `DM` that represents the topology
2472 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2473 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2474 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2475 
2476   Output Parameters:
2477 + 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)
2478 - 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)
2479 
2480   Level: advanced
2481 
2482   Notes:
2483   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.
2484 
2485   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 (or in case `sectiondm` is `NULL`) 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.
2486 
2487   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.
2488 
2489   Example using 2 processes:
2490 .vb
2491   NX (number of points on dm): 4
2492   sectionA                   : the on-disk section
2493   vecA                       : a vector associated with sectionA
2494   sectionB                   : sectiondm's local section constructed in this function
2495   vecB (local)               : a vector associated with sectiondm's local section
2496   vecB (global)              : a vector associated with sectiondm's global section
2497 
2498                                      rank 0    rank 1
2499   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2500   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2501   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2502   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2503   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2504   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2505   sectionB->atlasDof             :     1 0 1 | 1 3
2506   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2507   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2508   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2509 .ve
2510   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2511 
2512 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2513 @*/
2514 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2515 {
2516   PetscBool ishdf5;
2517 
2518   PetscFunctionBegin;
2519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2520   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2521   if (!sectiondm) sectiondm = dm;
2522   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2523   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2524   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2525   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2527   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2528   if (ishdf5) {
2529 #if defined(PETSC_HAVE_HDF5)
2530     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2531 #else
2532     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2533 #endif
2534   }
2535   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2536   PetscFunctionReturn(PETSC_SUCCESS);
2537 }
2538 
2539 /*@
2540   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2541 
2542   Collective
2543 
2544   Input Parameters:
2545 + dm        - The `DM` that represents the topology
2546 . viewer    - The `PetscViewer` that represents the on-disk vector data
2547 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2548 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2549 - vec       - The global vector to set values of
2550 
2551   Level: advanced
2552 
2553   Notes:
2554   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 (or in case `sectiondm` is `NULL`) 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.
2555 
2556   Calling sequence:
2557 .vb
2558        DMCreate(PETSC_COMM_WORLD, &dm);
2559        DMSetType(dm, DMPLEX);
2560        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2561        DMPlexTopologyLoad(dm, viewer, &sfX);
2562        DMClone(dm, &sectiondm);
2563        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2564        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2565        DMGetGlobalVector(sectiondm, &vec);
2566        PetscObjectSetName((PetscObject)vec, "vec_name");
2567        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2568        DMRestoreGlobalVector(sectiondm, &vec);
2569        PetscSFDestroy(&gsf);
2570        PetscSFDestroy(&sfX);
2571        DMDestroy(&sectiondm);
2572        DMDestroy(&dm);
2573 .ve
2574 
2575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2576           `PetscSF`, `PetscViewer`
2577 @*/
2578 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2579 {
2580   PetscBool ishdf5;
2581 
2582   PetscFunctionBegin;
2583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2584   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2585   if (!sectiondm) sectiondm = dm;
2586   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2587   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2588   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2589   /* Check consistency */
2590   {
2591     PetscSection section;
2592     PetscBool    includesConstraints;
2593     PetscInt     m, m1;
2594 
2595     PetscCall(VecGetLocalSize(vec, &m1));
2596     PetscCall(DMGetGlobalSection(sectiondm, &section));
2597     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2598     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2599     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2600     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2601   }
2602   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2603   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2604   if (ishdf5) {
2605 #if defined(PETSC_HAVE_HDF5)
2606     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2607 #else
2608     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2609 #endif
2610   }
2611   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2612   PetscFunctionReturn(PETSC_SUCCESS);
2613 }
2614 
2615 /*@
2616   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2617 
2618   Collective
2619 
2620   Input Parameters:
2621 + dm        - The `DM` that represents the topology
2622 . viewer    - The `PetscViewer` that represents the on-disk vector data
2623 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2624 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2625 - vec       - The local vector to set values of
2626 
2627   Level: advanced
2628 
2629   Notes:
2630   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 (or in case `sectiondm` is `NULL`) 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.
2631 
2632   Calling sequence:
2633 .vb
2634        DMCreate(PETSC_COMM_WORLD, &dm);
2635        DMSetType(dm, DMPLEX);
2636        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2637        DMPlexTopologyLoad(dm, viewer, &sfX);
2638        DMClone(dm, &sectiondm);
2639        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2640        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2641        DMGetLocalVector(sectiondm, &vec);
2642        PetscObjectSetName((PetscObject)vec, "vec_name");
2643        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2644        DMRestoreLocalVector(sectiondm, &vec);
2645        PetscSFDestroy(&lsf);
2646        PetscSFDestroy(&sfX);
2647        DMDestroy(&sectiondm);
2648        DMDestroy(&dm);
2649 .ve
2650 
2651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2652           `PetscSF`, `PetscViewer`
2653 @*/
2654 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2655 {
2656   PetscBool ishdf5;
2657 
2658   PetscFunctionBegin;
2659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2660   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2661   if (!sectiondm) sectiondm = dm;
2662   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2663   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2664   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2665   /* Check consistency */
2666   {
2667     PetscSection section;
2668     PetscBool    includesConstraints;
2669     PetscInt     m, m1;
2670 
2671     PetscCall(VecGetLocalSize(vec, &m1));
2672     PetscCall(DMGetLocalSection(sectiondm, &section));
2673     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2674     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2675     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2676     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2677   }
2678   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2679   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2680   if (ishdf5) {
2681 #if defined(PETSC_HAVE_HDF5)
2682     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2683 #else
2684     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2685 #endif
2686   }
2687   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2688   PetscFunctionReturn(PETSC_SUCCESS);
2689 }
2690 
2691 PetscErrorCode DMDestroy_Plex(DM dm)
2692 {
2693   DM_Plex *mesh = (DM_Plex *)dm->data;
2694 
2695   PetscFunctionBegin;
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2714   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2716   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2717   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2718   PetscCall(PetscFree(mesh->cones));
2719   PetscCall(PetscFree(mesh->coneOrientations));
2720   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2721   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2722   PetscCall(PetscFree(mesh->supports));
2723   PetscCall(PetscFree(mesh->cellTypes));
2724   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2725   PetscCall(PetscFree(mesh->tetgenOpts));
2726   PetscCall(PetscFree(mesh->triangleOpts));
2727   PetscCall(PetscFree(mesh->transformType));
2728   PetscCall(PetscFree(mesh->distributionName));
2729   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2730   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2731   PetscCall(ISDestroy(&mesh->subpointIS));
2732   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2733   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2734   if (mesh->periodic.face_sfs) {
2735     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2736     PetscCall(PetscFree(mesh->periodic.face_sfs));
2737   }
2738   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2739   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2740   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2741   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2742   PetscCall(ISDestroy(&mesh->anchorIS));
2743   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2744   PetscCall(PetscFree(mesh->parents));
2745   PetscCall(PetscFree(mesh->childIDs));
2746   PetscCall(PetscSectionDestroy(&mesh->childSection));
2747   PetscCall(PetscFree(mesh->children));
2748   PetscCall(DMDestroy(&mesh->referenceTree));
2749   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2750   PetscCall(PetscFree(mesh->neighbors));
2751   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2752   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2753   PetscCall(PetscFree(mesh));
2754   PetscFunctionReturn(PETSC_SUCCESS);
2755 }
2756 
2757 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2758 {
2759   PetscSection           sectionGlobal, sectionLocal;
2760   PetscInt               bs = -1, mbs;
2761   PetscInt               localSize, localStart = 0;
2762   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2763   MatType                mtype;
2764   ISLocalToGlobalMapping ltog;
2765 
2766   PetscFunctionBegin;
2767   PetscCall(MatInitializePackage());
2768   mtype = dm->mattype;
2769   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2770   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2771   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2772   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2773   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2774   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2775   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2776   PetscCall(MatSetType(*J, mtype));
2777   PetscCall(MatSetFromOptions(*J));
2778   PetscCall(MatGetBlockSize(*J, &mbs));
2779   if (mbs > 1) bs = mbs;
2780   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2781   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2782   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2783   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2784   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2785   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2786   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2787   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2788   if (!isShell) {
2789     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2790     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2791     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2792 
2793     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2794 
2795     PetscCall(PetscCalloc1(localSize, &pblocks));
2796     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2797     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2798     for (p = pStart; p < pEnd; ++p) {
2799       switch (dm->blocking_type) {
2800       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2801         PetscInt bdof, offset;
2802 
2803         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2804         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2805         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2806         for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2807         // Signal block concatenation
2808         if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2809         dof  = dof < 0 ? -(dof + 1) : dof;
2810         bdof = cdof && (dof - cdof) ? 1 : dof;
2811         if (dof) {
2812           if (bs < 0) {
2813             bs = bdof;
2814           } else if (bs != bdof) {
2815             bs = 1;
2816           }
2817         }
2818       } break;
2819       case DM_BLOCKING_FIELD_NODE: {
2820         for (PetscInt field = 0; field < num_fields; field++) {
2821           PetscInt num_comp, bdof, offset;
2822           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2823           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2824           if (dof < 0) continue;
2825           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2826           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2827           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);
2828           PetscInt num_nodes = dof / num_comp;
2829           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2830           // Handle possibly constant block size (unlikely)
2831           bdof = cdof && (dof - cdof) ? 1 : dof;
2832           if (dof) {
2833             if (bs < 0) {
2834               bs = bdof;
2835             } else if (bs != bdof) {
2836               bs = 1;
2837             }
2838           }
2839         }
2840       } break;
2841       }
2842     }
2843     /* Must have same blocksize on all procs (some might have no points) */
2844     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2845     bsLocal[1] = bs;
2846     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2847     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2848     else bs = bsMinMax[0];
2849     bs = PetscMax(1, bs);
2850     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2851     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2852       PetscCall(MatSetBlockSize(*J, bs));
2853       PetscCall(MatSetUp(*J));
2854     } else {
2855       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2856       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2857       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2858     }
2859     { // Consolidate blocks
2860       PetscInt nblocks = 0;
2861       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2862         if (pblocks[i] == 0) continue;
2863         // Negative block size indicates the blocks should be concatenated
2864         if (pblocks[i] < 0) {
2865           pblocks[i] = -pblocks[i];
2866           pblocks[nblocks - 1] += pblocks[i];
2867         } else {
2868           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2869         }
2870         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]);
2871       }
2872       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2873     }
2874     PetscCall(PetscFree(pblocks));
2875   }
2876   PetscCall(MatSetDM(*J, dm));
2877   PetscFunctionReturn(PETSC_SUCCESS);
2878 }
2879 
2880 /*@
2881   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2882 
2883   Not Collective
2884 
2885   Input Parameter:
2886 . dm - The `DMPLEX`
2887 
2888   Output Parameter:
2889 . subsection - The subdomain section
2890 
2891   Level: developer
2892 
2893 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2894 @*/
2895 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2896 {
2897   DM_Plex *mesh = (DM_Plex *)dm->data;
2898 
2899   PetscFunctionBegin;
2900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2901   if (!mesh->subdomainSection) {
2902     PetscSection section;
2903     PetscSF      sf;
2904 
2905     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2906     PetscCall(DMGetLocalSection(dm, &section));
2907     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2908     PetscCall(PetscSFDestroy(&sf));
2909   }
2910   *subsection = mesh->subdomainSection;
2911   PetscFunctionReturn(PETSC_SUCCESS);
2912 }
2913 
2914 /*@
2915   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2916 
2917   Not Collective
2918 
2919   Input Parameter:
2920 . dm - The `DMPLEX`
2921 
2922   Output Parameters:
2923 + pStart - The first mesh point
2924 - pEnd   - The upper bound for mesh points
2925 
2926   Level: beginner
2927 
2928 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2929 @*/
2930 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2931 {
2932   DM_Plex *mesh = (DM_Plex *)dm->data;
2933 
2934   PetscFunctionBegin;
2935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2936   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2937   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2938   PetscFunctionReturn(PETSC_SUCCESS);
2939 }
2940 
2941 /*@
2942   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2943 
2944   Not Collective
2945 
2946   Input Parameters:
2947 + dm     - The `DMPLEX`
2948 . pStart - The first mesh point
2949 - pEnd   - The upper bound for mesh points
2950 
2951   Level: beginner
2952 
2953 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2954 @*/
2955 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2956 {
2957   DM_Plex *mesh = (DM_Plex *)dm->data;
2958 
2959   PetscFunctionBegin;
2960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2961   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2962   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2963   PetscCall(PetscFree(mesh->cellTypes));
2964   PetscFunctionReturn(PETSC_SUCCESS);
2965 }
2966 
2967 /*@
2968   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2969 
2970   Not Collective
2971 
2972   Input Parameters:
2973 + dm - The `DMPLEX`
2974 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2975 
2976   Output Parameter:
2977 . size - The cone size for point `p`
2978 
2979   Level: beginner
2980 
2981 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2982 @*/
2983 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2984 {
2985   DM_Plex *mesh = (DM_Plex *)dm->data;
2986 
2987   PetscFunctionBegin;
2988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2989   PetscAssertPointer(size, 3);
2990   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2991   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2992   PetscFunctionReturn(PETSC_SUCCESS);
2993 }
2994 
2995 /*@
2996   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2997 
2998   Not Collective
2999 
3000   Input Parameters:
3001 + dm   - The `DMPLEX`
3002 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3003 - size - The cone size for point `p`
3004 
3005   Level: beginner
3006 
3007   Note:
3008   This should be called after `DMPlexSetChart()`.
3009 
3010 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3011 @*/
3012 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3013 {
3014   DM_Plex *mesh = (DM_Plex *)dm->data;
3015 
3016   PetscFunctionBegin;
3017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3018   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3019   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@C
3024   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm - The `DMPLEX`
3030 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3031 
3032   Output Parameter:
3033 . cone - An array of points which are on the in-edges for point `p`
3034 
3035   Level: beginner
3036 
3037   Fortran Notes:
3038   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3039   `DMPlexRestoreCone()` is not needed/available in C.
3040 
3041 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3042 @*/
3043 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3044 {
3045   DM_Plex *mesh = (DM_Plex *)dm->data;
3046   PetscInt off;
3047 
3048   PetscFunctionBegin;
3049   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3050   PetscAssertPointer(cone, 3);
3051   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3052   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3053   PetscFunctionReturn(PETSC_SUCCESS);
3054 }
3055 
3056 /*@C
3057   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3058 
3059   Not Collective
3060 
3061   Input Parameters:
3062 + dm - The `DMPLEX`
3063 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3064 
3065   Output Parameters:
3066 + pConesSection - `PetscSection` describing the layout of `pCones`
3067 - pCones        - An array of points which are on the in-edges for the point set `p`
3068 
3069   Level: intermediate
3070 
3071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3072 @*/
3073 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3074 {
3075   PetscSection cs, newcs;
3076   PetscInt    *cones;
3077   PetscInt    *newarr = NULL;
3078   PetscInt     n;
3079 
3080   PetscFunctionBegin;
3081   PetscCall(DMPlexGetCones(dm, &cones));
3082   PetscCall(DMPlexGetConeSection(dm, &cs));
3083   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3084   if (pConesSection) *pConesSection = newcs;
3085   if (pCones) {
3086     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3087     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3088   }
3089   PetscFunctionReturn(PETSC_SUCCESS);
3090 }
3091 
3092 /*@
3093   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3094 
3095   Not Collective
3096 
3097   Input Parameters:
3098 + dm     - The `DMPLEX`
3099 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3100 
3101   Output Parameter:
3102 . expandedPoints - An array of vertices recursively expanded from input points
3103 
3104   Level: advanced
3105 
3106   Notes:
3107   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3108 
3109   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3110 
3111 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3112           `DMPlexGetDepth()`, `IS`
3113 @*/
3114 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3115 {
3116   IS      *expandedPointsAll;
3117   PetscInt depth;
3118 
3119   PetscFunctionBegin;
3120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3121   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3122   PetscAssertPointer(expandedPoints, 3);
3123   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3124   *expandedPoints = expandedPointsAll[0];
3125   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3126   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3127   PetscFunctionReturn(PETSC_SUCCESS);
3128 }
3129 
3130 /*@
3131   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).
3132 
3133   Not Collective
3134 
3135   Input Parameters:
3136 + dm     - The `DMPLEX`
3137 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3138 
3139   Output Parameters:
3140 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3141 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3142 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3143 
3144   Level: advanced
3145 
3146   Notes:
3147   Like `DMPlexGetConeTuple()` but recursive.
3148 
3149   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.
3150   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3151 
3152   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\:
3153   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3154   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3155 
3156 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3157           `DMPlexGetDepth()`, `PetscSection`, `IS`
3158 @*/
3159 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3160 {
3161   const PetscInt *arr0 = NULL, *cone = NULL;
3162   PetscInt       *arr = NULL, *newarr = NULL;
3163   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3164   IS             *expandedPoints_;
3165   PetscSection   *sections_;
3166 
3167   PetscFunctionBegin;
3168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3169   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3170   if (depth) PetscAssertPointer(depth, 3);
3171   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3172   if (sections) PetscAssertPointer(sections, 5);
3173   PetscCall(ISGetLocalSize(points, &n));
3174   PetscCall(ISGetIndices(points, &arr0));
3175   PetscCall(DMPlexGetDepth(dm, &depth_));
3176   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3177   PetscCall(PetscCalloc1(depth_, &sections_));
3178   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3179   for (d = depth_ - 1; d >= 0; d--) {
3180     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3181     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3182     for (i = 0; i < n; i++) {
3183       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3184       if (arr[i] >= start && arr[i] < end) {
3185         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3186         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3187       } else {
3188         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3189       }
3190     }
3191     PetscCall(PetscSectionSetUp(sections_[d]));
3192     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3193     PetscCall(PetscMalloc1(newn, &newarr));
3194     for (i = 0; i < n; i++) {
3195       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3196       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3197       if (cn > 1) {
3198         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3199         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3200       } else {
3201         newarr[co] = arr[i];
3202       }
3203     }
3204     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3205     arr = newarr;
3206     n   = newn;
3207   }
3208   PetscCall(ISRestoreIndices(points, &arr0));
3209   *depth = depth_;
3210   if (expandedPoints) *expandedPoints = expandedPoints_;
3211   else {
3212     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3213     PetscCall(PetscFree(expandedPoints_));
3214   }
3215   if (sections) *sections = sections_;
3216   else {
3217     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3218     PetscCall(PetscFree(sections_));
3219   }
3220   PetscFunctionReturn(PETSC_SUCCESS);
3221 }
3222 
3223 /*@
3224   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3225 
3226   Not Collective
3227 
3228   Input Parameters:
3229 + dm     - The `DMPLEX`
3230 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3231 
3232   Output Parameters:
3233 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3234 . expandedPoints - (optional) An array of recursively expanded cones
3235 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3236 
3237   Level: advanced
3238 
3239   Note:
3240   See `DMPlexGetConeRecursive()`
3241 
3242 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3243           `DMPlexGetDepth()`, `IS`, `PetscSection`
3244 @*/
3245 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3246 {
3247   PetscInt d, depth_;
3248 
3249   PetscFunctionBegin;
3250   PetscCall(DMPlexGetDepth(dm, &depth_));
3251   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3252   if (depth) *depth = 0;
3253   if (expandedPoints) {
3254     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3255     PetscCall(PetscFree(*expandedPoints));
3256   }
3257   if (sections) {
3258     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3259     PetscCall(PetscFree(*sections));
3260   }
3261   PetscFunctionReturn(PETSC_SUCCESS);
3262 }
3263 
3264 /*@
3265   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
3266 
3267   Not Collective
3268 
3269   Input Parameters:
3270 + dm   - The `DMPLEX`
3271 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3272 - cone - An array of points which are on the in-edges for point `p`
3273 
3274   Level: beginner
3275 
3276   Note:
3277   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3278 
3279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3280 @*/
3281 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3282 {
3283   DM_Plex *mesh = (DM_Plex *)dm->data;
3284   PetscInt dof, off, c;
3285 
3286   PetscFunctionBegin;
3287   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3288   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3289   if (dof) PetscAssertPointer(cone, 3);
3290   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3291   if (PetscDefined(USE_DEBUG)) {
3292     PetscInt pStart, pEnd;
3293     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3294     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);
3295     for (c = 0; c < dof; ++c) {
3296       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);
3297       mesh->cones[off + c] = cone[c];
3298     }
3299   } else {
3300     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3301   }
3302   PetscFunctionReturn(PETSC_SUCCESS);
3303 }
3304 
3305 /*@C
3306   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3307 
3308   Not Collective
3309 
3310   Input Parameters:
3311 + dm - The `DMPLEX`
3312 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3313 
3314   Output Parameter:
3315 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3316                     integer giving the prescription for cone traversal.
3317 
3318   Level: beginner
3319 
3320   Note:
3321   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3322   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3323   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3324   with the identity.
3325 
3326   Fortran Notes:
3327   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3328   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3329 
3330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3331 @*/
3332 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3333 {
3334   DM_Plex *mesh = (DM_Plex *)dm->data;
3335   PetscInt off;
3336 
3337   PetscFunctionBegin;
3338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3339   if (PetscDefined(USE_DEBUG)) {
3340     PetscInt dof;
3341     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3342     if (dof) PetscAssertPointer(coneOrientation, 3);
3343   }
3344   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3345 
3346   *coneOrientation = &mesh->coneOrientations[off];
3347   PetscFunctionReturn(PETSC_SUCCESS);
3348 }
3349 
3350 /*@
3351   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3352 
3353   Not Collective
3354 
3355   Input Parameters:
3356 + dm              - The `DMPLEX`
3357 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3358 - coneOrientation - An array of orientations
3359 
3360   Level: beginner
3361 
3362   Notes:
3363   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3364 
3365   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3366 
3367 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3368 @*/
3369 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3370 {
3371   DM_Plex *mesh = (DM_Plex *)dm->data;
3372   PetscInt pStart, pEnd;
3373   PetscInt dof, off, c;
3374 
3375   PetscFunctionBegin;
3376   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3377   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3378   if (dof) PetscAssertPointer(coneOrientation, 3);
3379   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3380   if (PetscDefined(USE_DEBUG)) {
3381     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3382     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);
3383     for (c = 0; c < dof; ++c) {
3384       PetscInt cdof, o = coneOrientation[c];
3385 
3386       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3387       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);
3388       mesh->coneOrientations[off + c] = o;
3389     }
3390   } else {
3391     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3392   }
3393   PetscFunctionReturn(PETSC_SUCCESS);
3394 }
3395 
3396 /*@
3397   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3398 
3399   Not Collective
3400 
3401   Input Parameters:
3402 + dm        - The `DMPLEX`
3403 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3404 . conePos   - The local index in the cone where the point should be put
3405 - conePoint - The mesh point to insert
3406 
3407   Level: beginner
3408 
3409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3410 @*/
3411 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3412 {
3413   DM_Plex *mesh = (DM_Plex *)dm->data;
3414   PetscInt pStart, pEnd;
3415   PetscInt dof, off;
3416 
3417   PetscFunctionBegin;
3418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3419   if (PetscDefined(USE_DEBUG)) {
3420     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3421     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);
3422     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);
3423     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3424     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);
3425   }
3426   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3427   mesh->cones[off + conePos] = conePoint;
3428   PetscFunctionReturn(PETSC_SUCCESS);
3429 }
3430 
3431 /*@
3432   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3433 
3434   Not Collective
3435 
3436   Input Parameters:
3437 + dm              - The `DMPLEX`
3438 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3439 . conePos         - The local index in the cone where the point should be put
3440 - coneOrientation - The point orientation to insert
3441 
3442   Level: beginner
3443 
3444   Note:
3445   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3446 
3447 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3448 @*/
3449 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3450 {
3451   DM_Plex *mesh = (DM_Plex *)dm->data;
3452   PetscInt pStart, pEnd;
3453   PetscInt dof, off;
3454 
3455   PetscFunctionBegin;
3456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3457   if (PetscDefined(USE_DEBUG)) {
3458     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3459     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);
3460     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3461     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);
3462   }
3463   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3464   mesh->coneOrientations[off + conePos] = coneOrientation;
3465   PetscFunctionReturn(PETSC_SUCCESS);
3466 }
3467 
3468 /*@C
3469   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3470 
3471   Not collective
3472 
3473   Input Parameters:
3474 + dm - The DMPlex
3475 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3476 
3477   Output Parameters:
3478 + cone - An array of points which are on the in-edges for point `p`
3479 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3480         integer giving the prescription for cone traversal.
3481 
3482   Level: beginner
3483 
3484   Notes:
3485   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3486   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3487   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3488   with the identity.
3489 
3490   Fortran Notes:
3491   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3492   `DMPlexRestoreCone()` is not needed/available in C.
3493 
3494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3495 @*/
3496 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3497 {
3498   DM_Plex *mesh = (DM_Plex *)dm->data;
3499 
3500   PetscFunctionBegin;
3501   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3502   if (mesh->tr) {
3503     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3504   } else {
3505     PetscInt off;
3506     if (PetscDefined(USE_DEBUG)) {
3507       PetscInt dof;
3508       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3509       if (dof) {
3510         if (cone) PetscAssertPointer(cone, 3);
3511         if (ornt) PetscAssertPointer(ornt, 4);
3512       }
3513     }
3514     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3515     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3516     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3517   }
3518   PetscFunctionReturn(PETSC_SUCCESS);
3519 }
3520 
3521 /*@C
3522   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3523 
3524   Not Collective
3525 
3526   Input Parameters:
3527 + dm   - The DMPlex
3528 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3529 . cone - An array of points which are on the in-edges for point p
3530 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3531         integer giving the prescription for cone traversal.
3532 
3533   Level: beginner
3534 
3535   Notes:
3536   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3537   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3538   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3539   with the identity.
3540 
3541   Fortran Notes:
3542   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3543   `DMPlexRestoreCone()` is not needed/available in C.
3544 
3545 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3546 @*/
3547 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3548 {
3549   DM_Plex *mesh = (DM_Plex *)dm->data;
3550 
3551   PetscFunctionBegin;
3552   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3553   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3554   PetscFunctionReturn(PETSC_SUCCESS);
3555 }
3556 
3557 /*@
3558   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3559 
3560   Not Collective
3561 
3562   Input Parameters:
3563 + dm - The `DMPLEX`
3564 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3565 
3566   Output Parameter:
3567 . size - The support size for point `p`
3568 
3569   Level: beginner
3570 
3571 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3572 @*/
3573 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3574 {
3575   DM_Plex *mesh = (DM_Plex *)dm->data;
3576 
3577   PetscFunctionBegin;
3578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3579   PetscAssertPointer(size, 3);
3580   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3581   PetscFunctionReturn(PETSC_SUCCESS);
3582 }
3583 
3584 /*@
3585   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3586 
3587   Not Collective
3588 
3589   Input Parameters:
3590 + dm   - The `DMPLEX`
3591 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3592 - size - The support size for point `p`
3593 
3594   Level: beginner
3595 
3596   Note:
3597   This should be called after `DMPlexSetChart()`.
3598 
3599 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3600 @*/
3601 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3602 {
3603   DM_Plex *mesh = (DM_Plex *)dm->data;
3604 
3605   PetscFunctionBegin;
3606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3607   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3608   PetscFunctionReturn(PETSC_SUCCESS);
3609 }
3610 
3611 /*@C
3612   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3613 
3614   Not Collective
3615 
3616   Input Parameters:
3617 + dm - The `DMPLEX`
3618 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3619 
3620   Output Parameter:
3621 . support - An array of points which are on the out-edges for point `p`
3622 
3623   Level: beginner
3624 
3625   Fortran Notes:
3626   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3627   `DMPlexRestoreSupport()` is not needed/available in C.
3628 
3629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3630 @*/
3631 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3632 {
3633   DM_Plex *mesh = (DM_Plex *)dm->data;
3634   PetscInt off;
3635 
3636   PetscFunctionBegin;
3637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3638   PetscAssertPointer(support, 3);
3639   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3640   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3641   PetscFunctionReturn(PETSC_SUCCESS);
3642 }
3643 
3644 /*@
3645   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3646 
3647   Not Collective
3648 
3649   Input Parameters:
3650 + dm      - The `DMPLEX`
3651 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3652 - support - An array of points which are on the out-edges for point `p`
3653 
3654   Level: beginner
3655 
3656   Note:
3657   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3658 
3659 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3660 @*/
3661 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3662 {
3663   DM_Plex *mesh = (DM_Plex *)dm->data;
3664   PetscInt pStart, pEnd;
3665   PetscInt dof, off, c;
3666 
3667   PetscFunctionBegin;
3668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3669   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3670   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3671   if (dof) PetscAssertPointer(support, 3);
3672   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3673   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);
3674   for (c = 0; c < dof; ++c) {
3675     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);
3676     mesh->supports[off + c] = support[c];
3677   }
3678   PetscFunctionReturn(PETSC_SUCCESS);
3679 }
3680 
3681 /*@
3682   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3683 
3684   Not Collective
3685 
3686   Input Parameters:
3687 + dm           - The `DMPLEX`
3688 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3689 . supportPos   - The local index in the cone where the point should be put
3690 - supportPoint - The mesh point to insert
3691 
3692   Level: beginner
3693 
3694 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3695 @*/
3696 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3697 {
3698   DM_Plex *mesh = (DM_Plex *)dm->data;
3699   PetscInt pStart, pEnd;
3700   PetscInt dof, off;
3701 
3702   PetscFunctionBegin;
3703   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3704   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3705   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3706   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3707   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);
3708   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);
3709   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);
3710   mesh->supports[off + supportPos] = supportPoint;
3711   PetscFunctionReturn(PETSC_SUCCESS);
3712 }
3713 
3714 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3715 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3716 {
3717   switch (ct) {
3718   case DM_POLYTOPE_SEGMENT:
3719     if (o == -1) return -2;
3720     break;
3721   case DM_POLYTOPE_TRIANGLE:
3722     if (o == -3) return -1;
3723     if (o == -2) return -3;
3724     if (o == -1) return -2;
3725     break;
3726   case DM_POLYTOPE_QUADRILATERAL:
3727     if (o == -4) return -2;
3728     if (o == -3) return -1;
3729     if (o == -2) return -4;
3730     if (o == -1) return -3;
3731     break;
3732   default:
3733     return o;
3734   }
3735   return o;
3736 }
3737 
3738 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3739 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3740 {
3741   switch (ct) {
3742   case DM_POLYTOPE_SEGMENT:
3743     if ((o == -2) || (o == 1)) return -1;
3744     if (o == -1) return 0;
3745     break;
3746   case DM_POLYTOPE_TRIANGLE:
3747     if (o == -3) return -2;
3748     if (o == -2) return -1;
3749     if (o == -1) return -3;
3750     break;
3751   case DM_POLYTOPE_QUADRILATERAL:
3752     if (o == -4) return -2;
3753     if (o == -3) return -1;
3754     if (o == -2) return -4;
3755     if (o == -1) return -3;
3756     break;
3757   default:
3758     return o;
3759   }
3760   return o;
3761 }
3762 
3763 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3764 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3765 {
3766   PetscInt pStart, pEnd, p;
3767 
3768   PetscFunctionBegin;
3769   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3770   for (p = pStart; p < pEnd; ++p) {
3771     const PetscInt *cone, *ornt;
3772     PetscInt        coneSize, c;
3773 
3774     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3775     PetscCall(DMPlexGetCone(dm, p, &cone));
3776     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3777     for (c = 0; c < coneSize; ++c) {
3778       DMPolytopeType ct;
3779       const PetscInt o = ornt[c];
3780 
3781       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3782       switch (ct) {
3783       case DM_POLYTOPE_SEGMENT:
3784         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3785         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3786         break;
3787       case DM_POLYTOPE_TRIANGLE:
3788         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3789         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3790         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3791         break;
3792       case DM_POLYTOPE_QUADRILATERAL:
3793         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3794         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3795         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3796         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3797         break;
3798       default:
3799         break;
3800       }
3801     }
3802   }
3803   PetscFunctionReturn(PETSC_SUCCESS);
3804 }
3805 
3806 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3807 {
3808   DM_Plex *mesh = (DM_Plex *)dm->data;
3809 
3810   PetscFunctionBeginHot;
3811   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3812     if (useCone) {
3813       PetscCall(DMPlexGetConeSize(dm, p, size));
3814       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3815     } else {
3816       PetscCall(DMPlexGetSupportSize(dm, p, size));
3817       PetscCall(DMPlexGetSupport(dm, p, arr));
3818     }
3819   } else {
3820     if (useCone) {
3821       const PetscSection s   = mesh->coneSection;
3822       const PetscInt     ps  = p - s->pStart;
3823       const PetscInt     off = s->atlasOff[ps];
3824 
3825       *size = s->atlasDof[ps];
3826       *arr  = mesh->cones + off;
3827       *ornt = mesh->coneOrientations + off;
3828     } else {
3829       const PetscSection s   = mesh->supportSection;
3830       const PetscInt     ps  = p - s->pStart;
3831       const PetscInt     off = s->atlasOff[ps];
3832 
3833       *size = s->atlasDof[ps];
3834       *arr  = mesh->supports + off;
3835     }
3836   }
3837   PetscFunctionReturn(PETSC_SUCCESS);
3838 }
3839 
3840 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3841 {
3842   DM_Plex *mesh = (DM_Plex *)dm->data;
3843 
3844   PetscFunctionBeginHot;
3845   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3846     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3847   }
3848   PetscFunctionReturn(PETSC_SUCCESS);
3849 }
3850 
3851 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3852 {
3853   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3854   PetscInt       *closure;
3855   const PetscInt *tmp = NULL, *tmpO = NULL;
3856   PetscInt        off = 0, tmpSize, t;
3857 
3858   PetscFunctionBeginHot;
3859   if (ornt) {
3860     PetscCall(DMPlexGetCellType(dm, p, &ct));
3861     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
3862   }
3863   if (*points) {
3864     closure = *points;
3865   } else {
3866     PetscInt maxConeSize, maxSupportSize;
3867     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3868     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3869   }
3870   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3871   if (ct == DM_POLYTOPE_UNKNOWN) {
3872     closure[off++] = p;
3873     closure[off++] = 0;
3874     for (t = 0; t < tmpSize; ++t) {
3875       closure[off++] = tmp[t];
3876       closure[off++] = tmpO ? tmpO[t] : 0;
3877     }
3878   } else {
3879     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3880 
3881     /* We assume that cells with a valid type have faces with a valid type */
3882     closure[off++] = p;
3883     closure[off++] = ornt;
3884     for (t = 0; t < tmpSize; ++t) {
3885       DMPolytopeType ft;
3886 
3887       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3888       closure[off++] = tmp[arr[t]];
3889       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3890     }
3891   }
3892   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3893   if (numPoints) *numPoints = tmpSize + 1;
3894   if (points) *points = closure;
3895   PetscFunctionReturn(PETSC_SUCCESS);
3896 }
3897 
3898 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3899 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3900 {
3901   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3902   const PetscInt *cone, *ornt;
3903   PetscInt       *pts, *closure = NULL;
3904   DMPolytopeType  ft;
3905   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3906   PetscInt        dim, coneSize, c, d, clSize, cl;
3907 
3908   PetscFunctionBeginHot;
3909   PetscCall(DMGetDimension(dm, &dim));
3910   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3911   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3912   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3913   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3914   maxSize       = PetscMax(coneSeries, supportSeries);
3915   if (*points) {
3916     pts = *points;
3917   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3918   c        = 0;
3919   pts[c++] = point;
3920   pts[c++] = o;
3921   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3922   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3923   for (cl = 0; cl < clSize * 2; cl += 2) {
3924     pts[c++] = closure[cl];
3925     pts[c++] = closure[cl + 1];
3926   }
3927   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3928   for (cl = 0; cl < clSize * 2; cl += 2) {
3929     pts[c++] = closure[cl];
3930     pts[c++] = closure[cl + 1];
3931   }
3932   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3933   for (d = 2; d < coneSize; ++d) {
3934     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3935     pts[c++] = cone[arr[d * 2 + 0]];
3936     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3937   }
3938   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3939   if (dim >= 3) {
3940     for (d = 2; d < coneSize; ++d) {
3941       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3942       const PetscInt *fcone, *fornt;
3943       PetscInt        fconeSize, fc, i;
3944 
3945       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3946       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3947       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3948       for (fc = 0; fc < fconeSize; ++fc) {
3949         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3950         const PetscInt co = farr[fc * 2 + 1];
3951 
3952         for (i = 0; i < c; i += 2)
3953           if (pts[i] == cp) break;
3954         if (i == c) {
3955           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3956           pts[c++] = cp;
3957           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3958         }
3959       }
3960       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3961     }
3962   }
3963   *numPoints = c / 2;
3964   *points    = pts;
3965   PetscFunctionReturn(PETSC_SUCCESS);
3966 }
3967 
3968 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3969 {
3970   DMPolytopeType ct;
3971   PetscInt      *closure, *fifo;
3972   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3973   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3974   PetscInt       depth, maxSize;
3975 
3976   PetscFunctionBeginHot;
3977   PetscCall(DMPlexGetDepth(dm, &depth));
3978   if (depth == 1) {
3979     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3980     PetscFunctionReturn(PETSC_SUCCESS);
3981   }
3982   PetscCall(DMPlexGetCellType(dm, p, &ct));
3983   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
3984   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3985     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3986     PetscFunctionReturn(PETSC_SUCCESS);
3987   }
3988   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3989   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3990   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3991   maxSize       = PetscMax(coneSeries, supportSeries);
3992   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3993   if (*points) {
3994     closure = *points;
3995   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3996   closure[closureSize++] = p;
3997   closure[closureSize++] = ornt;
3998   fifo[fifoSize++]       = p;
3999   fifo[fifoSize++]       = ornt;
4000   fifo[fifoSize++]       = ct;
4001   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4002   while (fifoSize - fifoStart) {
4003     const PetscInt       q    = fifo[fifoStart++];
4004     const PetscInt       o    = fifo[fifoStart++];
4005     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4006     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4007     const PetscInt      *tmp, *tmpO = NULL;
4008     PetscInt             tmpSize, t;
4009 
4010     if (PetscDefined(USE_DEBUG)) {
4011       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4012       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);
4013     }
4014     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4015     for (t = 0; t < tmpSize; ++t) {
4016       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4017       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4018       const PetscInt cp = tmp[ip];
4019       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4020       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4021       PetscInt       c;
4022 
4023       /* Check for duplicate */
4024       for (c = 0; c < closureSize; c += 2) {
4025         if (closure[c] == cp) break;
4026       }
4027       if (c == closureSize) {
4028         closure[closureSize++] = cp;
4029         closure[closureSize++] = co;
4030         fifo[fifoSize++]       = cp;
4031         fifo[fifoSize++]       = co;
4032         fifo[fifoSize++]       = ct;
4033       }
4034     }
4035     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4036   }
4037   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4038   if (numPoints) *numPoints = closureSize / 2;
4039   if (points) *points = closure;
4040   PetscFunctionReturn(PETSC_SUCCESS);
4041 }
4042 
4043 /*@C
4044   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4045 
4046   Not Collective
4047 
4048   Input Parameters:
4049 + dm      - The `DMPLEX`
4050 . p       - The mesh point
4051 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4052 
4053   Input/Output Parameter:
4054 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4055            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4056 
4057   Output Parameter:
4058 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4059 
4060   Level: beginner
4061 
4062   Note:
4063   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4064 
4065   Fortran Notes:
4066   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4067 
4068 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4069 @*/
4070 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4071 {
4072   PetscFunctionBeginHot;
4073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4074   if (numPoints) PetscAssertPointer(numPoints, 4);
4075   if (points) PetscAssertPointer(points, 5);
4076   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@C
4081   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4082 
4083   Not Collective
4084 
4085   Input Parameters:
4086 + dm        - The `DMPLEX`
4087 . p         - The mesh point
4088 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4089 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4090 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4091 
4092   Level: beginner
4093 
4094   Note:
4095   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4096 
4097 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4098 @*/
4099 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4100 {
4101   PetscFunctionBeginHot;
4102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4103   if (numPoints) *numPoints = 0;
4104   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4105   PetscFunctionReturn(PETSC_SUCCESS);
4106 }
4107 
4108 /*@
4109   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4110 
4111   Not Collective
4112 
4113   Input Parameter:
4114 . dm - The `DMPLEX`
4115 
4116   Output Parameters:
4117 + maxConeSize    - The maximum number of in-edges
4118 - maxSupportSize - The maximum number of out-edges
4119 
4120   Level: beginner
4121 
4122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4123 @*/
4124 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4125 {
4126   DM_Plex *mesh = (DM_Plex *)dm->data;
4127 
4128   PetscFunctionBegin;
4129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4130   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4131   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4132   PetscFunctionReturn(PETSC_SUCCESS);
4133 }
4134 
4135 PetscErrorCode DMSetUp_Plex(DM dm)
4136 {
4137   DM_Plex *mesh = (DM_Plex *)dm->data;
4138   PetscInt size, maxSupportSize;
4139 
4140   PetscFunctionBegin;
4141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4142   PetscCall(PetscSectionSetUp(mesh->coneSection));
4143   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4144   PetscCall(PetscMalloc1(size, &mesh->cones));
4145   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4146   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4147   if (maxSupportSize) {
4148     PetscCall(PetscSectionSetUp(mesh->supportSection));
4149     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4150     PetscCall(PetscMalloc1(size, &mesh->supports));
4151   }
4152   PetscFunctionReturn(PETSC_SUCCESS);
4153 }
4154 
4155 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4156 {
4157   PetscFunctionBegin;
4158   if (subdm) PetscCall(DMClone(dm, subdm));
4159   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4160   if (subdm) (*subdm)->useNatural = dm->useNatural;
4161   if (dm->useNatural && dm->sfMigration) {
4162     PetscSF sfNatural;
4163 
4164     (*subdm)->sfMigration = dm->sfMigration;
4165     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4166     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4167     (*subdm)->sfNatural = sfNatural;
4168   }
4169   PetscFunctionReturn(PETSC_SUCCESS);
4170 }
4171 
4172 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4173 {
4174   PetscInt i = 0;
4175 
4176   PetscFunctionBegin;
4177   PetscCall(DMClone(dms[0], superdm));
4178   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4179   (*superdm)->useNatural = PETSC_FALSE;
4180   for (i = 0; i < len; i++) {
4181     if (dms[i]->useNatural && dms[i]->sfMigration) {
4182       PetscSF sfNatural;
4183 
4184       (*superdm)->sfMigration = dms[i]->sfMigration;
4185       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4186       (*superdm)->useNatural = PETSC_TRUE;
4187       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4188       (*superdm)->sfNatural = sfNatural;
4189       break;
4190     }
4191   }
4192   PetscFunctionReturn(PETSC_SUCCESS);
4193 }
4194 
4195 /*@
4196   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4197 
4198   Not Collective
4199 
4200   Input Parameter:
4201 . dm - The `DMPLEX`
4202 
4203   Level: beginner
4204 
4205   Note:
4206   This should be called after all calls to `DMPlexSetCone()`
4207 
4208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4209 @*/
4210 PetscErrorCode DMPlexSymmetrize(DM dm)
4211 {
4212   DM_Plex  *mesh = (DM_Plex *)dm->data;
4213   PetscInt *offsets;
4214   PetscInt  supportSize;
4215   PetscInt  pStart, pEnd, p;
4216 
4217   PetscFunctionBegin;
4218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4219   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4220   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4221   /* Calculate support sizes */
4222   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4223   for (p = pStart; p < pEnd; ++p) {
4224     PetscInt dof, off, c;
4225 
4226     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4227     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4228     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4229   }
4230   PetscCall(PetscSectionSetUp(mesh->supportSection));
4231   /* Calculate supports */
4232   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4233   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4234   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4235   for (p = pStart; p < pEnd; ++p) {
4236     PetscInt dof, off, c;
4237 
4238     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4239     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4240     for (c = off; c < off + dof; ++c) {
4241       const PetscInt q = mesh->cones[c];
4242       PetscInt       offS;
4243 
4244       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4245 
4246       mesh->supports[offS + offsets[q]] = p;
4247       ++offsets[q];
4248     }
4249   }
4250   PetscCall(PetscFree(offsets));
4251   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4252   PetscFunctionReturn(PETSC_SUCCESS);
4253 }
4254 
4255 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4256 {
4257   IS stratumIS;
4258 
4259   PetscFunctionBegin;
4260   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4261   if (PetscDefined(USE_DEBUG)) {
4262     PetscInt  qStart, qEnd, numLevels, level;
4263     PetscBool overlap = PETSC_FALSE;
4264     PetscCall(DMLabelGetNumValues(label, &numLevels));
4265     for (level = 0; level < numLevels; level++) {
4266       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4267       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4268         overlap = PETSC_TRUE;
4269         break;
4270       }
4271     }
4272     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);
4273   }
4274   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4275   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4276   PetscCall(ISDestroy(&stratumIS));
4277   PetscFunctionReturn(PETSC_SUCCESS);
4278 }
4279 
4280 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4281 {
4282   PetscInt *pMin, *pMax;
4283   PetscInt  pStart, pEnd;
4284   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4285 
4286   PetscFunctionBegin;
4287   {
4288     DMLabel label2;
4289 
4290     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4291     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4292   }
4293   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4294   for (PetscInt p = pStart; p < pEnd; ++p) {
4295     DMPolytopeType ct;
4296 
4297     PetscCall(DMPlexGetCellType(dm, p, &ct));
4298     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4299     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4300   }
4301   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4302   for (PetscInt d = dmin; d <= dmax; ++d) {
4303     pMin[d] = PETSC_MAX_INT;
4304     pMax[d] = PETSC_MIN_INT;
4305   }
4306   for (PetscInt p = pStart; p < pEnd; ++p) {
4307     DMPolytopeType ct;
4308     PetscInt       d;
4309 
4310     PetscCall(DMPlexGetCellType(dm, p, &ct));
4311     d       = DMPolytopeTypeGetDim(ct);
4312     pMin[d] = PetscMin(p, pMin[d]);
4313     pMax[d] = PetscMax(p, pMax[d]);
4314   }
4315   for (PetscInt d = dmin; d <= dmax; ++d) {
4316     if (pMin[d] > pMax[d]) continue;
4317     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4318   }
4319   PetscCall(PetscFree2(pMin, pMax));
4320   PetscFunctionReturn(PETSC_SUCCESS);
4321 }
4322 
4323 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4324 {
4325   PetscInt pStart, pEnd;
4326   PetscInt numRoots = 0, numLeaves = 0;
4327 
4328   PetscFunctionBegin;
4329   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4330   {
4331     /* Initialize roots and count leaves */
4332     PetscInt sMin = PETSC_MAX_INT;
4333     PetscInt sMax = PETSC_MIN_INT;
4334     PetscInt coneSize, supportSize;
4335 
4336     for (PetscInt p = pStart; p < pEnd; ++p) {
4337       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4338       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4339       if (!coneSize && supportSize) {
4340         sMin = PetscMin(p, sMin);
4341         sMax = PetscMax(p, sMax);
4342         ++numRoots;
4343       } else if (!supportSize && coneSize) {
4344         ++numLeaves;
4345       } else if (!supportSize && !coneSize) {
4346         /* Isolated points */
4347         sMin = PetscMin(p, sMin);
4348         sMax = PetscMax(p, sMax);
4349       }
4350     }
4351     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4352   }
4353 
4354   if (numRoots + numLeaves == (pEnd - pStart)) {
4355     PetscInt sMin = PETSC_MAX_INT;
4356     PetscInt sMax = PETSC_MIN_INT;
4357     PetscInt coneSize, supportSize;
4358 
4359     for (PetscInt p = pStart; p < pEnd; ++p) {
4360       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4361       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4362       if (!supportSize && coneSize) {
4363         sMin = PetscMin(p, sMin);
4364         sMax = PetscMax(p, sMax);
4365       }
4366     }
4367     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4368   } else {
4369     PetscInt level = 0;
4370     PetscInt qStart, qEnd;
4371 
4372     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4373     while (qEnd > qStart) {
4374       PetscInt sMin = PETSC_MAX_INT;
4375       PetscInt sMax = PETSC_MIN_INT;
4376 
4377       for (PetscInt q = qStart; q < qEnd; ++q) {
4378         const PetscInt *support;
4379         PetscInt        supportSize;
4380 
4381         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4382         PetscCall(DMPlexGetSupport(dm, q, &support));
4383         for (PetscInt s = 0; s < supportSize; ++s) {
4384           sMin = PetscMin(support[s], sMin);
4385           sMax = PetscMax(support[s], sMax);
4386         }
4387       }
4388       PetscCall(DMLabelGetNumValues(label, &level));
4389       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4390       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4391     }
4392   }
4393   PetscFunctionReturn(PETSC_SUCCESS);
4394 }
4395 
4396 /*@
4397   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4398 
4399   Collective
4400 
4401   Input Parameter:
4402 . dm - The `DMPLEX`
4403 
4404   Level: beginner
4405 
4406   Notes:
4407   The strata group all points of the same grade, and this function calculates the strata. This
4408   grade can be seen as the height (or depth) of the point in the DAG.
4409 
4410   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4411   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4412   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4413   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4414   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4415   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4416   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4417 
4418   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4419   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4420   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
4421   to interpolate only that one (e0), so that
4422 .vb
4423   cone(c0) = {e0, v2}
4424   cone(e0) = {v0, v1}
4425 .ve
4426   If `DMPlexStratify()` is run on this mesh, it will give depths
4427 .vb
4428    depth 0 = {v0, v1, v2}
4429    depth 1 = {e0, c0}
4430 .ve
4431   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4432 
4433   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4434 
4435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4436 @*/
4437 PetscErrorCode DMPlexStratify(DM dm)
4438 {
4439   DM_Plex  *mesh = (DM_Plex *)dm->data;
4440   DMLabel   label;
4441   PetscBool flg = PETSC_FALSE;
4442 
4443   PetscFunctionBegin;
4444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4445   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4446 
4447   // Create depth label
4448   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4449   PetscCall(DMCreateLabel(dm, "depth"));
4450   PetscCall(DMPlexGetDepthLabel(dm, &label));
4451 
4452   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4453   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4454   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4455 
4456   { /* just in case there is an empty process */
4457     PetscInt numValues, maxValues = 0, v;
4458 
4459     PetscCall(DMLabelGetNumValues(label, &numValues));
4460     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4461     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4462   }
4463   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4464   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4465   PetscFunctionReturn(PETSC_SUCCESS);
4466 }
4467 
4468 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4469 {
4470   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4471   PetscInt       dim, depth, pheight, coneSize;
4472 
4473   PetscFunctionBeginHot;
4474   PetscCall(DMGetDimension(dm, &dim));
4475   PetscCall(DMPlexGetDepth(dm, &depth));
4476   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4477   pheight = depth - pdepth;
4478   if (depth <= 1) {
4479     switch (pdepth) {
4480     case 0:
4481       ct = DM_POLYTOPE_POINT;
4482       break;
4483     case 1:
4484       switch (coneSize) {
4485       case 2:
4486         ct = DM_POLYTOPE_SEGMENT;
4487         break;
4488       case 3:
4489         ct = DM_POLYTOPE_TRIANGLE;
4490         break;
4491       case 4:
4492         switch (dim) {
4493         case 2:
4494           ct = DM_POLYTOPE_QUADRILATERAL;
4495           break;
4496         case 3:
4497           ct = DM_POLYTOPE_TETRAHEDRON;
4498           break;
4499         default:
4500           break;
4501         }
4502         break;
4503       case 5:
4504         ct = DM_POLYTOPE_PYRAMID;
4505         break;
4506       case 6:
4507         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4508         break;
4509       case 8:
4510         ct = DM_POLYTOPE_HEXAHEDRON;
4511         break;
4512       default:
4513         break;
4514       }
4515     }
4516   } else {
4517     if (pdepth == 0) {
4518       ct = DM_POLYTOPE_POINT;
4519     } else if (pheight == 0) {
4520       switch (dim) {
4521       case 1:
4522         switch (coneSize) {
4523         case 2:
4524           ct = DM_POLYTOPE_SEGMENT;
4525           break;
4526         default:
4527           break;
4528         }
4529         break;
4530       case 2:
4531         switch (coneSize) {
4532         case 3:
4533           ct = DM_POLYTOPE_TRIANGLE;
4534           break;
4535         case 4:
4536           ct = DM_POLYTOPE_QUADRILATERAL;
4537           break;
4538         default:
4539           break;
4540         }
4541         break;
4542       case 3:
4543         switch (coneSize) {
4544         case 4:
4545           ct = DM_POLYTOPE_TETRAHEDRON;
4546           break;
4547         case 5: {
4548           const PetscInt *cone;
4549           PetscInt        faceConeSize;
4550 
4551           PetscCall(DMPlexGetCone(dm, p, &cone));
4552           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4553           switch (faceConeSize) {
4554           case 3:
4555             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4556             break;
4557           case 4:
4558             ct = DM_POLYTOPE_PYRAMID;
4559             break;
4560           }
4561         } break;
4562         case 6:
4563           ct = DM_POLYTOPE_HEXAHEDRON;
4564           break;
4565         default:
4566           break;
4567         }
4568         break;
4569       default:
4570         break;
4571       }
4572     } else if (pheight > 0) {
4573       switch (coneSize) {
4574       case 2:
4575         ct = DM_POLYTOPE_SEGMENT;
4576         break;
4577       case 3:
4578         ct = DM_POLYTOPE_TRIANGLE;
4579         break;
4580       case 4:
4581         ct = DM_POLYTOPE_QUADRILATERAL;
4582         break;
4583       default:
4584         break;
4585       }
4586     }
4587   }
4588   *pt = ct;
4589   PetscFunctionReturn(PETSC_SUCCESS);
4590 }
4591 
4592 /*@
4593   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4594 
4595   Collective
4596 
4597   Input Parameter:
4598 . dm - The `DMPLEX`
4599 
4600   Level: developer
4601 
4602   Note:
4603   This function is normally called automatically when a cell type is requested. It creates an
4604   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4605   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4606 
4607   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4608 
4609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4610 @*/
4611 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4612 {
4613   DM_Plex *mesh;
4614   DMLabel  ctLabel;
4615   PetscInt pStart, pEnd, p;
4616 
4617   PetscFunctionBegin;
4618   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4619   mesh = (DM_Plex *)dm->data;
4620   PetscCall(DMCreateLabel(dm, "celltype"));
4621   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4622   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4623   PetscCall(PetscFree(mesh->cellTypes));
4624   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4625   for (p = pStart; p < pEnd; ++p) {
4626     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4627     PetscInt       pdepth;
4628 
4629     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4630     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4631     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4632     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4633     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4634   }
4635   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4636   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4637   PetscFunctionReturn(PETSC_SUCCESS);
4638 }
4639 
4640 /*@C
4641   DMPlexGetJoin - Get an array for the join of the set of points
4642 
4643   Not Collective
4644 
4645   Input Parameters:
4646 + dm        - The `DMPLEX` object
4647 . numPoints - The number of input points for the join
4648 - points    - The input points
4649 
4650   Output Parameters:
4651 + numCoveredPoints - The number of points in the join
4652 - coveredPoints    - The points in the join
4653 
4654   Level: intermediate
4655 
4656   Note:
4657   Currently, this is restricted to a single level join
4658 
4659   Fortran Notes:
4660   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4661 
4662 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4663 @*/
4664 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4665 {
4666   DM_Plex  *mesh = (DM_Plex *)dm->data;
4667   PetscInt *join[2];
4668   PetscInt  joinSize, i = 0;
4669   PetscInt  dof, off, p, c, m;
4670   PetscInt  maxSupportSize;
4671 
4672   PetscFunctionBegin;
4673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4674   PetscAssertPointer(points, 3);
4675   PetscAssertPointer(numCoveredPoints, 4);
4676   PetscAssertPointer(coveredPoints, 5);
4677   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4678   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4679   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4680   /* Copy in support of first point */
4681   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4682   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4683   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4684   /* Check each successive support */
4685   for (p = 1; p < numPoints; ++p) {
4686     PetscInt newJoinSize = 0;
4687 
4688     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4689     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4690     for (c = 0; c < dof; ++c) {
4691       const PetscInt point = mesh->supports[off + c];
4692 
4693       for (m = 0; m < joinSize; ++m) {
4694         if (point == join[i][m]) {
4695           join[1 - i][newJoinSize++] = point;
4696           break;
4697         }
4698       }
4699     }
4700     joinSize = newJoinSize;
4701     i        = 1 - i;
4702   }
4703   *numCoveredPoints = joinSize;
4704   *coveredPoints    = join[i];
4705   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4706   PetscFunctionReturn(PETSC_SUCCESS);
4707 }
4708 
4709 /*@C
4710   DMPlexRestoreJoin - Restore an array for the join of the set of points
4711 
4712   Not Collective
4713 
4714   Input Parameters:
4715 + dm        - The `DMPLEX` object
4716 . numPoints - The number of input points for the join
4717 - points    - The input points
4718 
4719   Output Parameters:
4720 + numCoveredPoints - The number of points in the join
4721 - coveredPoints    - The points in the join
4722 
4723   Level: intermediate
4724 
4725   Fortran Notes:
4726   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4727 
4728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4729 @*/
4730 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4731 {
4732   PetscFunctionBegin;
4733   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4734   if (points) PetscAssertPointer(points, 3);
4735   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4736   PetscAssertPointer(coveredPoints, 5);
4737   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4738   if (numCoveredPoints) *numCoveredPoints = 0;
4739   PetscFunctionReturn(PETSC_SUCCESS);
4740 }
4741 
4742 /*@C
4743   DMPlexGetFullJoin - Get an array for the join of the set of points
4744 
4745   Not Collective
4746 
4747   Input Parameters:
4748 + dm        - The `DMPLEX` object
4749 . numPoints - The number of input points for the join
4750 - points    - The input points
4751 
4752   Output Parameters:
4753 + numCoveredPoints - The number of points in the join
4754 - coveredPoints    - The points in the join
4755 
4756   Level: intermediate
4757 
4758   Fortran Notes:
4759   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4760 
4761 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4762 @*/
4763 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4764 {
4765   PetscInt *offsets, **closures;
4766   PetscInt *join[2];
4767   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4768   PetscInt  p, d, c, m, ms;
4769 
4770   PetscFunctionBegin;
4771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4772   PetscAssertPointer(points, 3);
4773   PetscAssertPointer(numCoveredPoints, 4);
4774   PetscAssertPointer(coveredPoints, 5);
4775 
4776   PetscCall(DMPlexGetDepth(dm, &depth));
4777   PetscCall(PetscCalloc1(numPoints, &closures));
4778   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4779   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4780   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4781   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4782   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4783 
4784   for (p = 0; p < numPoints; ++p) {
4785     PetscInt closureSize;
4786 
4787     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4788 
4789     offsets[p * (depth + 2) + 0] = 0;
4790     for (d = 0; d < depth + 1; ++d) {
4791       PetscInt pStart, pEnd, i;
4792 
4793       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4794       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4795         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4796           offsets[p * (depth + 2) + d + 1] = i;
4797           break;
4798         }
4799       }
4800       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4801     }
4802     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);
4803   }
4804   for (d = 0; d < depth + 1; ++d) {
4805     PetscInt dof;
4806 
4807     /* Copy in support of first point */
4808     dof = offsets[d + 1] - offsets[d];
4809     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4810     /* Check each successive cone */
4811     for (p = 1; p < numPoints && joinSize; ++p) {
4812       PetscInt newJoinSize = 0;
4813 
4814       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4815       for (c = 0; c < dof; ++c) {
4816         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4817 
4818         for (m = 0; m < joinSize; ++m) {
4819           if (point == join[i][m]) {
4820             join[1 - i][newJoinSize++] = point;
4821             break;
4822           }
4823         }
4824       }
4825       joinSize = newJoinSize;
4826       i        = 1 - i;
4827     }
4828     if (joinSize) break;
4829   }
4830   *numCoveredPoints = joinSize;
4831   *coveredPoints    = join[i];
4832   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4833   PetscCall(PetscFree(closures));
4834   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4835   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4836   PetscFunctionReturn(PETSC_SUCCESS);
4837 }
4838 
4839 /*@C
4840   DMPlexGetMeet - Get an array for the meet of the set of points
4841 
4842   Not Collective
4843 
4844   Input Parameters:
4845 + dm        - The `DMPLEX` object
4846 . numPoints - The number of input points for the meet
4847 - points    - The input points
4848 
4849   Output Parameters:
4850 + numCoveringPoints - The number of points in the meet
4851 - coveringPoints    - The points in the meet
4852 
4853   Level: intermediate
4854 
4855   Note:
4856   Currently, this is restricted to a single level meet
4857 
4858   Fortran Notes:
4859   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4860 
4861 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4862 @*/
4863 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4864 {
4865   DM_Plex  *mesh = (DM_Plex *)dm->data;
4866   PetscInt *meet[2];
4867   PetscInt  meetSize, i = 0;
4868   PetscInt  dof, off, p, c, m;
4869   PetscInt  maxConeSize;
4870 
4871   PetscFunctionBegin;
4872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4873   PetscAssertPointer(points, 3);
4874   PetscAssertPointer(numCoveringPoints, 4);
4875   PetscAssertPointer(coveringPoints, 5);
4876   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4877   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4878   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4879   /* Copy in cone of first point */
4880   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4881   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4882   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4883   /* Check each successive cone */
4884   for (p = 1; p < numPoints; ++p) {
4885     PetscInt newMeetSize = 0;
4886 
4887     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4888     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4889     for (c = 0; c < dof; ++c) {
4890       const PetscInt point = mesh->cones[off + c];
4891 
4892       for (m = 0; m < meetSize; ++m) {
4893         if (point == meet[i][m]) {
4894           meet[1 - i][newMeetSize++] = point;
4895           break;
4896         }
4897       }
4898     }
4899     meetSize = newMeetSize;
4900     i        = 1 - i;
4901   }
4902   *numCoveringPoints = meetSize;
4903   *coveringPoints    = meet[i];
4904   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4905   PetscFunctionReturn(PETSC_SUCCESS);
4906 }
4907 
4908 /*@C
4909   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4910 
4911   Not Collective
4912 
4913   Input Parameters:
4914 + dm        - The `DMPLEX` object
4915 . numPoints - The number of input points for the meet
4916 - points    - The input points
4917 
4918   Output Parameters:
4919 + numCoveredPoints - The number of points in the meet
4920 - coveredPoints    - The points in the meet
4921 
4922   Level: intermediate
4923 
4924   Fortran Notes:
4925   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4926 
4927 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4928 @*/
4929 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4930 {
4931   PetscFunctionBegin;
4932   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4933   if (points) PetscAssertPointer(points, 3);
4934   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4935   PetscAssertPointer(coveredPoints, 5);
4936   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4937   if (numCoveredPoints) *numCoveredPoints = 0;
4938   PetscFunctionReturn(PETSC_SUCCESS);
4939 }
4940 
4941 /*@C
4942   DMPlexGetFullMeet - Get an array for the meet of the set of points
4943 
4944   Not Collective
4945 
4946   Input Parameters:
4947 + dm        - The `DMPLEX` object
4948 . numPoints - The number of input points for the meet
4949 - points    - The input points
4950 
4951   Output Parameters:
4952 + numCoveredPoints - The number of points in the meet
4953 - coveredPoints    - The points in the meet
4954 
4955   Level: intermediate
4956 
4957   Fortran Notes:
4958   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4959 
4960 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4961 @*/
4962 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4963 {
4964   PetscInt *offsets, **closures;
4965   PetscInt *meet[2];
4966   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4967   PetscInt  p, h, c, m, mc;
4968 
4969   PetscFunctionBegin;
4970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4971   PetscAssertPointer(points, 3);
4972   PetscAssertPointer(numCoveredPoints, 4);
4973   PetscAssertPointer(coveredPoints, 5);
4974 
4975   PetscCall(DMPlexGetDepth(dm, &height));
4976   PetscCall(PetscMalloc1(numPoints, &closures));
4977   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4978   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4979   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4980   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4981   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4982 
4983   for (p = 0; p < numPoints; ++p) {
4984     PetscInt closureSize;
4985 
4986     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4987 
4988     offsets[p * (height + 2) + 0] = 0;
4989     for (h = 0; h < height + 1; ++h) {
4990       PetscInt pStart, pEnd, i;
4991 
4992       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4993       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4994         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4995           offsets[p * (height + 2) + h + 1] = i;
4996           break;
4997         }
4998       }
4999       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5000     }
5001     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);
5002   }
5003   for (h = 0; h < height + 1; ++h) {
5004     PetscInt dof;
5005 
5006     /* Copy in cone of first point */
5007     dof = offsets[h + 1] - offsets[h];
5008     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5009     /* Check each successive cone */
5010     for (p = 1; p < numPoints && meetSize; ++p) {
5011       PetscInt newMeetSize = 0;
5012 
5013       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5014       for (c = 0; c < dof; ++c) {
5015         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5016 
5017         for (m = 0; m < meetSize; ++m) {
5018           if (point == meet[i][m]) {
5019             meet[1 - i][newMeetSize++] = point;
5020             break;
5021           }
5022         }
5023       }
5024       meetSize = newMeetSize;
5025       i        = 1 - i;
5026     }
5027     if (meetSize) break;
5028   }
5029   *numCoveredPoints = meetSize;
5030   *coveredPoints    = meet[i];
5031   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5032   PetscCall(PetscFree(closures));
5033   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5034   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5035   PetscFunctionReturn(PETSC_SUCCESS);
5036 }
5037 
5038 /*@C
5039   DMPlexEqual - Determine if two `DM` have the same topology
5040 
5041   Not Collective
5042 
5043   Input Parameters:
5044 + dmA - A `DMPLEX` object
5045 - dmB - A `DMPLEX` object
5046 
5047   Output Parameter:
5048 . equal - `PETSC_TRUE` if the topologies are identical
5049 
5050   Level: intermediate
5051 
5052   Note:
5053   We are not solving graph isomorphism, so we do not permute.
5054 
5055 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5056 @*/
5057 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5058 {
5059   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5060 
5061   PetscFunctionBegin;
5062   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5063   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5064   PetscAssertPointer(equal, 3);
5065 
5066   *equal = PETSC_FALSE;
5067   PetscCall(DMPlexGetDepth(dmA, &depth));
5068   PetscCall(DMPlexGetDepth(dmB, &depthB));
5069   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5070   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5071   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5072   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5073   for (p = pStart; p < pEnd; ++p) {
5074     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5075     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5076 
5077     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5078     PetscCall(DMPlexGetCone(dmA, p, &cone));
5079     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5080     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5081     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5082     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5083     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5084     for (c = 0; c < coneSize; ++c) {
5085       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5086       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5087     }
5088     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5089     PetscCall(DMPlexGetSupport(dmA, p, &support));
5090     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5091     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5092     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5093     for (s = 0; s < supportSize; ++s) {
5094       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5095     }
5096   }
5097   *equal = PETSC_TRUE;
5098   PetscFunctionReturn(PETSC_SUCCESS);
5099 }
5100 
5101 /*@C
5102   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5103 
5104   Not Collective
5105 
5106   Input Parameters:
5107 + dm         - The `DMPLEX`
5108 . cellDim    - The cell dimension
5109 - numCorners - The number of vertices on a cell
5110 
5111   Output Parameter:
5112 . numFaceVertices - The number of vertices on a face
5113 
5114   Level: developer
5115 
5116   Note:
5117   Of course this can only work for a restricted set of symmetric shapes
5118 
5119 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5120 @*/
5121 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5122 {
5123   MPI_Comm comm;
5124 
5125   PetscFunctionBegin;
5126   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5127   PetscAssertPointer(numFaceVertices, 4);
5128   switch (cellDim) {
5129   case 0:
5130     *numFaceVertices = 0;
5131     break;
5132   case 1:
5133     *numFaceVertices = 1;
5134     break;
5135   case 2:
5136     switch (numCorners) {
5137     case 3:                 /* triangle */
5138       *numFaceVertices = 2; /* Edge has 2 vertices */
5139       break;
5140     case 4:                 /* quadrilateral */
5141       *numFaceVertices = 2; /* Edge has 2 vertices */
5142       break;
5143     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5144       *numFaceVertices = 3; /* Edge has 3 vertices */
5145       break;
5146     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5147       *numFaceVertices = 3; /* Edge has 3 vertices */
5148       break;
5149     default:
5150       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5151     }
5152     break;
5153   case 3:
5154     switch (numCorners) {
5155     case 4:                 /* tetradehdron */
5156       *numFaceVertices = 3; /* Face has 3 vertices */
5157       break;
5158     case 6:                 /* tet cohesive cells */
5159       *numFaceVertices = 4; /* Face has 4 vertices */
5160       break;
5161     case 8:                 /* hexahedron */
5162       *numFaceVertices = 4; /* Face has 4 vertices */
5163       break;
5164     case 9:                 /* tet cohesive Lagrange cells */
5165       *numFaceVertices = 6; /* Face has 6 vertices */
5166       break;
5167     case 10:                /* quadratic tetrahedron */
5168       *numFaceVertices = 6; /* Face has 6 vertices */
5169       break;
5170     case 12:                /* hex cohesive Lagrange cells */
5171       *numFaceVertices = 6; /* Face has 6 vertices */
5172       break;
5173     case 18:                /* quadratic tet cohesive Lagrange cells */
5174       *numFaceVertices = 6; /* Face has 6 vertices */
5175       break;
5176     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5177       *numFaceVertices = 9; /* Face has 9 vertices */
5178       break;
5179     default:
5180       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5181     }
5182     break;
5183   default:
5184     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5185   }
5186   PetscFunctionReturn(PETSC_SUCCESS);
5187 }
5188 
5189 /*@
5190   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5191 
5192   Not Collective
5193 
5194   Input Parameter:
5195 . dm - The `DMPLEX` object
5196 
5197   Output Parameter:
5198 . depthLabel - The `DMLabel` recording point depth
5199 
5200   Level: developer
5201 
5202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5203 @*/
5204 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5205 {
5206   PetscFunctionBegin;
5207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5208   PetscAssertPointer(depthLabel, 2);
5209   *depthLabel = dm->depthLabel;
5210   PetscFunctionReturn(PETSC_SUCCESS);
5211 }
5212 
5213 /*@
5214   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5215 
5216   Not Collective
5217 
5218   Input Parameter:
5219 . dm - The `DMPLEX` object
5220 
5221   Output Parameter:
5222 . depth - The number of strata (breadth first levels) in the DAG
5223 
5224   Level: developer
5225 
5226   Notes:
5227   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5228 
5229   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5230 
5231   An empty mesh gives -1.
5232 
5233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5234 @*/
5235 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5236 {
5237   DM_Plex *mesh = (DM_Plex *)dm->data;
5238   DMLabel  label;
5239   PetscInt d = 0;
5240 
5241   PetscFunctionBegin;
5242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5243   PetscAssertPointer(depth, 2);
5244   if (mesh->tr) {
5245     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5246   } else {
5247     PetscCall(DMPlexGetDepthLabel(dm, &label));
5248     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5249     *depth = d - 1;
5250   }
5251   PetscFunctionReturn(PETSC_SUCCESS);
5252 }
5253 
5254 /*@
5255   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5256 
5257   Not Collective
5258 
5259   Input Parameters:
5260 + dm    - The `DMPLEX` object
5261 - depth - The requested depth
5262 
5263   Output Parameters:
5264 + start - The first point at this `depth`
5265 - end   - One beyond the last point at this `depth`
5266 
5267   Level: developer
5268 
5269   Notes:
5270   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5271   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5272   higher dimension, e.g., "edges".
5273 
5274 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5275 @*/
5276 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5277 {
5278   DM_Plex *mesh = (DM_Plex *)dm->data;
5279   DMLabel  label;
5280   PetscInt pStart, pEnd;
5281 
5282   PetscFunctionBegin;
5283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5284   if (start) {
5285     PetscAssertPointer(start, 3);
5286     *start = 0;
5287   }
5288   if (end) {
5289     PetscAssertPointer(end, 4);
5290     *end = 0;
5291   }
5292   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5293   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5294   if (depth < 0) {
5295     if (start) *start = pStart;
5296     if (end) *end = pEnd;
5297     PetscFunctionReturn(PETSC_SUCCESS);
5298   }
5299   if (mesh->tr) {
5300     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5301   } else {
5302     PetscCall(DMPlexGetDepthLabel(dm, &label));
5303     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5304     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5305   }
5306   PetscFunctionReturn(PETSC_SUCCESS);
5307 }
5308 
5309 /*@
5310   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5311 
5312   Not Collective
5313 
5314   Input Parameters:
5315 + dm     - The `DMPLEX` object
5316 - height - The requested height
5317 
5318   Output Parameters:
5319 + start - The first point at this `height`
5320 - end   - One beyond the last point at this `height`
5321 
5322   Level: developer
5323 
5324   Notes:
5325   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5326   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5327   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5328 
5329 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5330 @*/
5331 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5332 {
5333   DMLabel  label;
5334   PetscInt depth, pStart, pEnd;
5335 
5336   PetscFunctionBegin;
5337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5338   if (start) {
5339     PetscAssertPointer(start, 3);
5340     *start = 0;
5341   }
5342   if (end) {
5343     PetscAssertPointer(end, 4);
5344     *end = 0;
5345   }
5346   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5347   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5348   if (height < 0) {
5349     if (start) *start = pStart;
5350     if (end) *end = pEnd;
5351     PetscFunctionReturn(PETSC_SUCCESS);
5352   }
5353   PetscCall(DMPlexGetDepthLabel(dm, &label));
5354   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5355   else PetscCall(DMGetDimension(dm, &depth));
5356   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5357   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5358   PetscFunctionReturn(PETSC_SUCCESS);
5359 }
5360 
5361 /*@
5362   DMPlexGetPointDepth - Get the `depth` of a given point
5363 
5364   Not Collective
5365 
5366   Input Parameters:
5367 + dm    - The `DMPLEX` object
5368 - point - The point
5369 
5370   Output Parameter:
5371 . depth - The depth of the `point`
5372 
5373   Level: intermediate
5374 
5375 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5376 @*/
5377 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5378 {
5379   PetscFunctionBegin;
5380   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5381   PetscAssertPointer(depth, 3);
5382   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5383   PetscFunctionReturn(PETSC_SUCCESS);
5384 }
5385 
5386 /*@
5387   DMPlexGetPointHeight - Get the `height` of a given point
5388 
5389   Not Collective
5390 
5391   Input Parameters:
5392 + dm    - The `DMPLEX` object
5393 - point - The point
5394 
5395   Output Parameter:
5396 . height - The height of the `point`
5397 
5398   Level: intermediate
5399 
5400 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5401 @*/
5402 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5403 {
5404   PetscInt n, pDepth;
5405 
5406   PetscFunctionBegin;
5407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5408   PetscAssertPointer(height, 3);
5409   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5410   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5411   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5412   PetscFunctionReturn(PETSC_SUCCESS);
5413 }
5414 
5415 /*@
5416   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5417 
5418   Not Collective
5419 
5420   Input Parameter:
5421 . dm - The `DMPLEX` object
5422 
5423   Output Parameter:
5424 . celltypeLabel - The `DMLabel` recording cell polytope type
5425 
5426   Level: developer
5427 
5428   Note:
5429   This function will trigger automatica computation of cell types. This can be disabled by calling
5430   `DMCreateLabel`(dm, "celltype") beforehand.
5431 
5432 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5433 @*/
5434 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5435 {
5436   PetscFunctionBegin;
5437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5438   PetscAssertPointer(celltypeLabel, 2);
5439   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5440   *celltypeLabel = dm->celltypeLabel;
5441   PetscFunctionReturn(PETSC_SUCCESS);
5442 }
5443 
5444 /*@
5445   DMPlexGetCellType - Get the polytope type of a given cell
5446 
5447   Not Collective
5448 
5449   Input Parameters:
5450 + dm   - The `DMPLEX` object
5451 - cell - The cell
5452 
5453   Output Parameter:
5454 . celltype - The polytope type of the cell
5455 
5456   Level: intermediate
5457 
5458 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5459 @*/
5460 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5461 {
5462   DM_Plex *mesh = (DM_Plex *)dm->data;
5463   DMLabel  label;
5464   PetscInt ct;
5465 
5466   PetscFunctionBegin;
5467   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5468   PetscAssertPointer(celltype, 3);
5469   if (mesh->tr) {
5470     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5471   } else {
5472     PetscInt pStart, pEnd;
5473 
5474     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5475     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5476       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5477       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5478       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5479       for (PetscInt p = pStart; p < pEnd; p++) {
5480         PetscCall(DMLabelGetValue(label, p, &ct));
5481         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5482       }
5483     }
5484     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5485     if (PetscDefined(USE_DEBUG)) {
5486       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5487       PetscCall(DMLabelGetValue(label, cell, &ct));
5488       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5489       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5490     }
5491   }
5492   PetscFunctionReturn(PETSC_SUCCESS);
5493 }
5494 
5495 /*@
5496   DMPlexSetCellType - Set the polytope type of a given cell
5497 
5498   Not Collective
5499 
5500   Input Parameters:
5501 + dm       - The `DMPLEX` object
5502 . cell     - The cell
5503 - celltype - The polytope type of the cell
5504 
5505   Level: advanced
5506 
5507   Note:
5508   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5509   is executed. This function will override the computed type. However, if automatic classification will not succeed
5510   and a user wants to manually specify all types, the classification must be disabled by calling
5511   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5512 
5513 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5514 @*/
5515 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5516 {
5517   DM_Plex *mesh = (DM_Plex *)dm->data;
5518   DMLabel  label;
5519   PetscInt pStart, pEnd;
5520 
5521   PetscFunctionBegin;
5522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5523   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5524   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5525   PetscCall(DMLabelSetValue(label, cell, celltype));
5526   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5527   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5528   PetscFunctionReturn(PETSC_SUCCESS);
5529 }
5530 
5531 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5532 {
5533   PetscSection section;
5534   PetscInt     maxHeight;
5535   const char  *prefix;
5536 
5537   PetscFunctionBegin;
5538   PetscCall(DMClone(dm, cdm));
5539   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5540   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5541   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5542   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5543   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5544   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5545   PetscCall(DMSetLocalSection(*cdm, section));
5546   PetscCall(PetscSectionDestroy(&section));
5547 
5548   PetscCall(DMSetNumFields(*cdm, 1));
5549   PetscCall(DMCreateDS(*cdm));
5550   (*cdm)->cloneOpts = PETSC_TRUE;
5551   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5552   PetscFunctionReturn(PETSC_SUCCESS);
5553 }
5554 
5555 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5556 {
5557   Vec coordsLocal, cellCoordsLocal;
5558   DM  coordsDM, cellCoordsDM;
5559 
5560   PetscFunctionBegin;
5561   *field = NULL;
5562   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5563   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5564   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5565   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5566   if (coordsLocal && coordsDM) {
5567     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5568     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5569   }
5570   PetscFunctionReturn(PETSC_SUCCESS);
5571 }
5572 
5573 /*@C
5574   DMPlexGetConeSection - Return a section which describes the layout of cone data
5575 
5576   Not Collective
5577 
5578   Input Parameter:
5579 . dm - The `DMPLEX` object
5580 
5581   Output Parameter:
5582 . section - The `PetscSection` object
5583 
5584   Level: developer
5585 
5586 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5587 @*/
5588 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5589 {
5590   DM_Plex *mesh = (DM_Plex *)dm->data;
5591 
5592   PetscFunctionBegin;
5593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5594   if (section) *section = mesh->coneSection;
5595   PetscFunctionReturn(PETSC_SUCCESS);
5596 }
5597 
5598 /*@C
5599   DMPlexGetSupportSection - Return a section which describes the layout of support data
5600 
5601   Not Collective
5602 
5603   Input Parameter:
5604 . dm - The `DMPLEX` object
5605 
5606   Output Parameter:
5607 . section - The `PetscSection` object
5608 
5609   Level: developer
5610 
5611 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5612 @*/
5613 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5614 {
5615   DM_Plex *mesh = (DM_Plex *)dm->data;
5616 
5617   PetscFunctionBegin;
5618   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5619   if (section) *section = mesh->supportSection;
5620   PetscFunctionReturn(PETSC_SUCCESS);
5621 }
5622 
5623 /*@C
5624   DMPlexGetCones - Return cone data
5625 
5626   Not Collective
5627 
5628   Input Parameter:
5629 . dm - The `DMPLEX` object
5630 
5631   Output Parameter:
5632 . cones - The cone for each point
5633 
5634   Level: developer
5635 
5636 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5637 @*/
5638 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5639 {
5640   DM_Plex *mesh = (DM_Plex *)dm->data;
5641 
5642   PetscFunctionBegin;
5643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5644   if (cones) *cones = mesh->cones;
5645   PetscFunctionReturn(PETSC_SUCCESS);
5646 }
5647 
5648 /*@C
5649   DMPlexGetConeOrientations - Return cone orientation data
5650 
5651   Not Collective
5652 
5653   Input Parameter:
5654 . dm - The `DMPLEX` object
5655 
5656   Output Parameter:
5657 . coneOrientations - The array of cone orientations for all points
5658 
5659   Level: developer
5660 
5661   Notes:
5662   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5663 
5664   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5665 
5666 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5667 @*/
5668 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5669 {
5670   DM_Plex *mesh = (DM_Plex *)dm->data;
5671 
5672   PetscFunctionBegin;
5673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5674   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5675   PetscFunctionReturn(PETSC_SUCCESS);
5676 }
5677 
5678 /******************************** FEM Support **********************************/
5679 
5680 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5681 {
5682   PetscInt depth;
5683 
5684   PetscFunctionBegin;
5685   PetscCall(DMPlexGetDepth(plex, &depth));
5686   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5687   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5688   PetscFunctionReturn(PETSC_SUCCESS);
5689 }
5690 
5691 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5692 {
5693   PetscInt depth;
5694 
5695   PetscFunctionBegin;
5696   PetscCall(DMPlexGetDepth(plex, &depth));
5697   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5698   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5699   PetscFunctionReturn(PETSC_SUCCESS);
5700 }
5701 
5702 /*
5703  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5704  representing a line in the section.
5705 */
5706 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5707 {
5708   PetscObject  obj;
5709   PetscClassId id;
5710   PetscFE      fe = NULL;
5711 
5712   PetscFunctionBeginHot;
5713   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5714   PetscCall(DMGetField(dm, field, NULL, &obj));
5715   PetscCall(PetscObjectGetClassId(obj, &id));
5716   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5717 
5718   if (!fe) {
5719     /* Assume the full interpolated mesh is in the chart; lines in particular */
5720     /* An order k SEM disc has k-1 dofs on an edge */
5721     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5722     *k = *k / *Nc + 1;
5723   } else {
5724     PetscInt       dual_space_size, dim;
5725     PetscDualSpace dsp;
5726 
5727     PetscCall(DMGetDimension(dm, &dim));
5728     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5729     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5730     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5731     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5732     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5733   }
5734   PetscFunctionReturn(PETSC_SUCCESS);
5735 }
5736 
5737 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5738 {
5739   PetscFunctionBeginHot;
5740   if (tensor) {
5741     *dof = PetscPowInt(k + 1, dim);
5742   } else {
5743     switch (dim) {
5744     case 1:
5745       *dof = k + 1;
5746       break;
5747     case 2:
5748       *dof = ((k + 1) * (k + 2)) / 2;
5749       break;
5750     case 3:
5751       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5752       break;
5753     default:
5754       *dof = 0;
5755     }
5756   }
5757   PetscFunctionReturn(PETSC_SUCCESS);
5758 }
5759 
5760 /*@
5761 
5762   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5763   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5764   section provided (or the section of the `DM`).
5765 
5766   Input Parameters:
5767 + dm      - The `DM`
5768 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5769 - section - The `PetscSection` to reorder, or `NULL` for the default section
5770 
5771   Example:
5772   A typical interpolated single-quad mesh might order points as
5773 .vb
5774   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5775 
5776   v4 -- e6 -- v3
5777   |           |
5778   e7    c0    e8
5779   |           |
5780   v1 -- e5 -- v2
5781 .ve
5782 
5783   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5784   dofs in the order of points, e.g.,
5785 .vb
5786     c0 -> [0,1,2,3]
5787     v1 -> [4]
5788     ...
5789     e5 -> [8, 9]
5790 .ve
5791 
5792   which corresponds to the dofs
5793 .vb
5794     6   10  11  7
5795     13  2   3   15
5796     12  0   1   14
5797     4   8   9   5
5798 .ve
5799 
5800   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5801 .vb
5802   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5803 .ve
5804 
5805   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5806 .vb
5807    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5808 .ve
5809 
5810   Level: developer
5811 
5812   Notes:
5813   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5814   degree of the basis.
5815 
5816   This is required to run with libCEED.
5817 
5818 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5819 @*/
5820 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5821 {
5822   DMLabel   label;
5823   PetscInt  dim, depth = -1, eStart = -1, Nf;
5824   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5825 
5826   PetscFunctionBegin;
5827   PetscCall(DMGetDimension(dm, &dim));
5828   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5829   if (point < 0) {
5830     PetscInt sStart, sEnd;
5831 
5832     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5833     point = sEnd - sStart ? sStart : point;
5834   }
5835   PetscCall(DMPlexGetDepthLabel(dm, &label));
5836   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5837   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5838   if (depth == 1) {
5839     eStart = point;
5840   } else if (depth == dim) {
5841     const PetscInt *cone;
5842 
5843     PetscCall(DMPlexGetCone(dm, point, &cone));
5844     if (dim == 2) eStart = cone[0];
5845     else if (dim == 3) {
5846       const PetscInt *cone2;
5847       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5848       eStart = cone2[0];
5849     } 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);
5850   } 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);
5851 
5852   PetscCall(PetscSectionGetNumFields(section, &Nf));
5853   for (PetscInt d = 1; d <= dim; d++) {
5854     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5855     PetscInt *perm;
5856 
5857     for (f = 0; f < Nf; ++f) {
5858       PetscInt dof;
5859 
5860       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5861       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5862       if (!continuous && d < dim) continue;
5863       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5864       size += dof * Nc;
5865     }
5866     PetscCall(PetscMalloc1(size, &perm));
5867     for (f = 0; f < Nf; ++f) {
5868       switch (d) {
5869       case 1:
5870         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5871         if (!continuous && d < dim) continue;
5872         /*
5873          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5874          We want              [ vtx0; edge of length k-1; vtx1 ]
5875          */
5876         if (continuous) {
5877           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5878           for (i = 0; i < k - 1; i++)
5879             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5880           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5881           foffset = offset;
5882         } else {
5883           PetscInt dof;
5884 
5885           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5886           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5887           foffset = offset;
5888         }
5889         break;
5890       case 2:
5891         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5892         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5893         if (!continuous && d < dim) continue;
5894         /* The SEM order is
5895 
5896          v_lb, {e_b}, v_rb,
5897          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5898          v_lt, reverse {e_t}, v_rt
5899          */
5900         if (continuous) {
5901           const PetscInt of   = 0;
5902           const PetscInt oeb  = of + PetscSqr(k - 1);
5903           const PetscInt oer  = oeb + (k - 1);
5904           const PetscInt oet  = oer + (k - 1);
5905           const PetscInt oel  = oet + (k - 1);
5906           const PetscInt ovlb = oel + (k - 1);
5907           const PetscInt ovrb = ovlb + 1;
5908           const PetscInt ovrt = ovrb + 1;
5909           const PetscInt ovlt = ovrt + 1;
5910           PetscInt       o;
5911 
5912           /* bottom */
5913           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5914           for (o = oeb; o < oer; ++o)
5915             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5916           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5917           /* middle */
5918           for (i = 0; i < k - 1; ++i) {
5919             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5920             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5921               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5922             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5923           }
5924           /* top */
5925           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5926           for (o = oel - 1; o >= oet; --o)
5927             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5928           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5929           foffset = offset;
5930         } else {
5931           PetscInt dof;
5932 
5933           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5934           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5935           foffset = offset;
5936         }
5937         break;
5938       case 3:
5939         /* The original hex closure is
5940 
5941          {c,
5942          f_b, f_t, f_f, f_b, f_r, f_l,
5943          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5944          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5945          */
5946         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5947         if (!continuous && d < dim) continue;
5948         /* The SEM order is
5949          Bottom Slice
5950          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5951          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5952          v_blb, {e_bb}, v_brb,
5953 
5954          Middle Slice (j)
5955          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5956          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5957          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5958 
5959          Top Slice
5960          v_tlf, {e_tf}, v_trf,
5961          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5962          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5963          */
5964         if (continuous) {
5965           const PetscInt oc    = 0;
5966           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5967           const PetscInt oft   = ofb + PetscSqr(k - 1);
5968           const PetscInt off   = oft + PetscSqr(k - 1);
5969           const PetscInt ofk   = off + PetscSqr(k - 1);
5970           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5971           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5972           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5973           const PetscInt oebb  = oebl + (k - 1);
5974           const PetscInt oebr  = oebb + (k - 1);
5975           const PetscInt oebf  = oebr + (k - 1);
5976           const PetscInt oetf  = oebf + (k - 1);
5977           const PetscInt oetr  = oetf + (k - 1);
5978           const PetscInt oetb  = oetr + (k - 1);
5979           const PetscInt oetl  = oetb + (k - 1);
5980           const PetscInt oerf  = oetl + (k - 1);
5981           const PetscInt oelf  = oerf + (k - 1);
5982           const PetscInt oelb  = oelf + (k - 1);
5983           const PetscInt oerb  = oelb + (k - 1);
5984           const PetscInt ovblf = oerb + (k - 1);
5985           const PetscInt ovblb = ovblf + 1;
5986           const PetscInt ovbrb = ovblb + 1;
5987           const PetscInt ovbrf = ovbrb + 1;
5988           const PetscInt ovtlf = ovbrf + 1;
5989           const PetscInt ovtrf = ovtlf + 1;
5990           const PetscInt ovtrb = ovtrf + 1;
5991           const PetscInt ovtlb = ovtrb + 1;
5992           PetscInt       o, n;
5993 
5994           /* Bottom Slice */
5995           /*   bottom */
5996           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5997           for (o = oetf - 1; o >= oebf; --o)
5998             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5999           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6000           /*   middle */
6001           for (i = 0; i < k - 1; ++i) {
6002             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6003             for (n = 0; n < k - 1; ++n) {
6004               o = ofb + n * (k - 1) + i;
6005               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6006             }
6007             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6008           }
6009           /*   top */
6010           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6011           for (o = oebb; o < oebr; ++o)
6012             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6013           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6014 
6015           /* Middle Slice */
6016           for (j = 0; j < k - 1; ++j) {
6017             /*   bottom */
6018             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6019             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6020               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6021             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6022             /*   middle */
6023             for (i = 0; i < k - 1; ++i) {
6024               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6025               for (n = 0; n < k - 1; ++n)
6026                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6027               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6028             }
6029             /*   top */
6030             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6031             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6032               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6033             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6034           }
6035 
6036           /* Top Slice */
6037           /*   bottom */
6038           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6039           for (o = oetf; o < oetr; ++o)
6040             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6041           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6042           /*   middle */
6043           for (i = 0; i < k - 1; ++i) {
6044             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6045             for (n = 0; n < k - 1; ++n)
6046               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6047             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6048           }
6049           /*   top */
6050           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6051           for (o = oetl - 1; o >= oetb; --o)
6052             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6053           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6054 
6055           foffset = offset;
6056         } else {
6057           PetscInt dof;
6058 
6059           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6060           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6061           foffset = offset;
6062         }
6063         break;
6064       default:
6065         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6066       }
6067     }
6068     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6069     /* Check permutation */
6070     {
6071       PetscInt *check;
6072 
6073       PetscCall(PetscMalloc1(size, &check));
6074       for (i = 0; i < size; ++i) {
6075         check[i] = -1;
6076         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6077       }
6078       for (i = 0; i < size; ++i) check[perm[i]] = i;
6079       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6080       PetscCall(PetscFree(check));
6081     }
6082     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6083     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6084       PetscInt *loc_perm;
6085       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6086       for (PetscInt i = 0; i < size; i++) {
6087         loc_perm[i]        = perm[i];
6088         loc_perm[size + i] = size + perm[i];
6089       }
6090       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6091     }
6092   }
6093   PetscFunctionReturn(PETSC_SUCCESS);
6094 }
6095 
6096 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6097 {
6098   PetscDS  prob;
6099   PetscInt depth, Nf, h;
6100   DMLabel  label;
6101 
6102   PetscFunctionBeginHot;
6103   PetscCall(DMGetDS(dm, &prob));
6104   Nf      = prob->Nf;
6105   label   = dm->depthLabel;
6106   *dspace = NULL;
6107   if (field < Nf) {
6108     PetscObject disc = prob->disc[field];
6109 
6110     if (disc->classid == PETSCFE_CLASSID) {
6111       PetscDualSpace dsp;
6112 
6113       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6114       PetscCall(DMLabelGetNumValues(label, &depth));
6115       PetscCall(DMLabelGetValue(label, point, &h));
6116       h = depth - 1 - h;
6117       if (h) {
6118         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6119       } else {
6120         *dspace = dsp;
6121       }
6122     }
6123   }
6124   PetscFunctionReturn(PETSC_SUCCESS);
6125 }
6126 
6127 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6128 {
6129   PetscScalar       *array;
6130   const PetscScalar *vArray;
6131   const PetscInt    *cone, *coneO;
6132   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6133 
6134   PetscFunctionBeginHot;
6135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6136   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6137   PetscCall(DMPlexGetCone(dm, point, &cone));
6138   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6139   if (!values || !*values) {
6140     if ((point >= pStart) && (point < pEnd)) {
6141       PetscInt dof;
6142 
6143       PetscCall(PetscSectionGetDof(section, point, &dof));
6144       size += dof;
6145     }
6146     for (p = 0; p < numPoints; ++p) {
6147       const PetscInt cp = cone[p];
6148       PetscInt       dof;
6149 
6150       if ((cp < pStart) || (cp >= pEnd)) continue;
6151       PetscCall(PetscSectionGetDof(section, cp, &dof));
6152       size += dof;
6153     }
6154     if (!values) {
6155       if (csize) *csize = size;
6156       PetscFunctionReturn(PETSC_SUCCESS);
6157     }
6158     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6159   } else {
6160     array = *values;
6161   }
6162   size = 0;
6163   PetscCall(VecGetArrayRead(v, &vArray));
6164   if ((point >= pStart) && (point < pEnd)) {
6165     PetscInt           dof, off, d;
6166     const PetscScalar *varr;
6167 
6168     PetscCall(PetscSectionGetDof(section, point, &dof));
6169     PetscCall(PetscSectionGetOffset(section, point, &off));
6170     varr = PetscSafePointerPlusOffset(vArray, off);
6171     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6172     size += dof;
6173   }
6174   for (p = 0; p < numPoints; ++p) {
6175     const PetscInt     cp = cone[p];
6176     PetscInt           o  = coneO[p];
6177     PetscInt           dof, off, d;
6178     const PetscScalar *varr;
6179 
6180     if ((cp < pStart) || (cp >= pEnd)) continue;
6181     PetscCall(PetscSectionGetDof(section, cp, &dof));
6182     PetscCall(PetscSectionGetOffset(section, cp, &off));
6183     varr = PetscSafePointerPlusOffset(vArray, off);
6184     if (o >= 0) {
6185       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6186     } else {
6187       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6188     }
6189     size += dof;
6190   }
6191   PetscCall(VecRestoreArrayRead(v, &vArray));
6192   if (!*values) {
6193     if (csize) *csize = size;
6194     *values = array;
6195   } else {
6196     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6197     *csize = size;
6198   }
6199   PetscFunctionReturn(PETSC_SUCCESS);
6200 }
6201 
6202 /* Compress out points not in the section */
6203 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6204 {
6205   const PetscInt np = *numPoints;
6206   PetscInt       pStart, pEnd, p, q;
6207 
6208   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6209   for (p = 0, q = 0; p < np; ++p) {
6210     const PetscInt r = points[p * 2];
6211     if ((r >= pStart) && (r < pEnd)) {
6212       points[q * 2]     = r;
6213       points[q * 2 + 1] = points[p * 2 + 1];
6214       ++q;
6215     }
6216   }
6217   *numPoints = q;
6218   return PETSC_SUCCESS;
6219 }
6220 
6221 /* Compressed closure does not apply closure permutation */
6222 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6223 {
6224   const PetscInt *cla = NULL;
6225   PetscInt        np, *pts = NULL;
6226 
6227   PetscFunctionBeginHot;
6228   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6229   if (!ornt && *clPoints) {
6230     PetscInt dof, off;
6231 
6232     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6233     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6234     PetscCall(ISGetIndices(*clPoints, &cla));
6235     np  = dof / 2;
6236     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6237   } else {
6238     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6239     PetscCall(CompressPoints_Private(section, &np, pts));
6240   }
6241   *numPoints = np;
6242   *points    = pts;
6243   *clp       = cla;
6244   PetscFunctionReturn(PETSC_SUCCESS);
6245 }
6246 
6247 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6248 {
6249   PetscFunctionBeginHot;
6250   if (!*clPoints) {
6251     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6252   } else {
6253     PetscCall(ISRestoreIndices(*clPoints, clp));
6254   }
6255   *numPoints = 0;
6256   *points    = NULL;
6257   *clSec     = NULL;
6258   *clPoints  = NULL;
6259   *clp       = NULL;
6260   PetscFunctionReturn(PETSC_SUCCESS);
6261 }
6262 
6263 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6264 {
6265   PetscInt            offset = 0, p;
6266   const PetscInt    **perms  = NULL;
6267   const PetscScalar **flips  = NULL;
6268 
6269   PetscFunctionBeginHot;
6270   *size = 0;
6271   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6272   for (p = 0; p < numPoints; p++) {
6273     const PetscInt     point = points[2 * p];
6274     const PetscInt    *perm  = perms ? perms[p] : NULL;
6275     const PetscScalar *flip  = flips ? flips[p] : NULL;
6276     PetscInt           dof, off, d;
6277     const PetscScalar *varr;
6278 
6279     PetscCall(PetscSectionGetDof(section, point, &dof));
6280     PetscCall(PetscSectionGetOffset(section, point, &off));
6281     varr = PetscSafePointerPlusOffset(vArray, off);
6282     if (clperm) {
6283       if (perm) {
6284         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6285       } else {
6286         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6287       }
6288       if (flip) {
6289         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6290       }
6291     } else {
6292       if (perm) {
6293         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6294       } else {
6295         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6296       }
6297       if (flip) {
6298         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6299       }
6300     }
6301     offset += dof;
6302   }
6303   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6304   *size = offset;
6305   PetscFunctionReturn(PETSC_SUCCESS);
6306 }
6307 
6308 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[])
6309 {
6310   PetscInt offset = 0, f;
6311 
6312   PetscFunctionBeginHot;
6313   *size = 0;
6314   for (f = 0; f < numFields; ++f) {
6315     PetscInt            p;
6316     const PetscInt    **perms = NULL;
6317     const PetscScalar **flips = NULL;
6318 
6319     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6320     for (p = 0; p < numPoints; p++) {
6321       const PetscInt     point = points[2 * p];
6322       PetscInt           fdof, foff, b;
6323       const PetscScalar *varr;
6324       const PetscInt    *perm = perms ? perms[p] : NULL;
6325       const PetscScalar *flip = flips ? flips[p] : NULL;
6326 
6327       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6328       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6329       varr = &vArray[foff];
6330       if (clperm) {
6331         if (perm) {
6332           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6333         } else {
6334           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6335         }
6336         if (flip) {
6337           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6338         }
6339       } else {
6340         if (perm) {
6341           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6342         } else {
6343           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6344         }
6345         if (flip) {
6346           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6347         }
6348       }
6349       offset += fdof;
6350     }
6351     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6352   }
6353   *size = offset;
6354   PetscFunctionReturn(PETSC_SUCCESS);
6355 }
6356 
6357 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6358 {
6359   PetscSection    clSection;
6360   IS              clPoints;
6361   PetscInt       *points = NULL;
6362   const PetscInt *clp, *perm = NULL;
6363   PetscInt        depth, numFields, numPoints, asize;
6364 
6365   PetscFunctionBeginHot;
6366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6367   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6368   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6369   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6370   PetscCall(DMPlexGetDepth(dm, &depth));
6371   PetscCall(PetscSectionGetNumFields(section, &numFields));
6372   if (depth == 1 && numFields < 2) {
6373     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6374     PetscFunctionReturn(PETSC_SUCCESS);
6375   }
6376   /* Get points */
6377   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6378   /* Get sizes */
6379   asize = 0;
6380   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6381     PetscInt dof;
6382     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6383     asize += dof;
6384   }
6385   if (values) {
6386     const PetscScalar *vArray;
6387     PetscInt           size;
6388 
6389     if (*values) {
6390       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);
6391     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6392     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6393     PetscCall(VecGetArrayRead(v, &vArray));
6394     /* Get values */
6395     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6396     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6397     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6398     /* Cleanup array */
6399     PetscCall(VecRestoreArrayRead(v, &vArray));
6400   }
6401   if (csize) *csize = asize;
6402   /* Cleanup points */
6403   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6404   PetscFunctionReturn(PETSC_SUCCESS);
6405 }
6406 
6407 /*@C
6408   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6409 
6410   Not collective
6411 
6412   Input Parameters:
6413 + dm      - The `DM`
6414 . section - The section describing the layout in `v`, or `NULL` to use the default section
6415 . v       - The local vector
6416 - point   - The point in the `DM`
6417 
6418   Input/Output Parameters:
6419 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6420 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6421            if the user provided `NULL`, it is a borrowed array and should not be freed
6422 
6423   Level: intermediate
6424 
6425   Notes:
6426   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6427   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6428   assembly function, and a user may already have allocated storage for this operation.
6429 
6430   A typical use could be
6431 .vb
6432    values = NULL;
6433    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6434    for (cl = 0; cl < clSize; ++cl) {
6435      <Compute on closure>
6436    }
6437    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6438 .ve
6439   or
6440 .vb
6441    PetscMalloc1(clMaxSize, &values);
6442    for (p = pStart; p < pEnd; ++p) {
6443      clSize = clMaxSize;
6444      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6445      for (cl = 0; cl < clSize; ++cl) {
6446        <Compute on closure>
6447      }
6448    }
6449    PetscFree(values);
6450 .ve
6451 
6452   Fortran Notes:
6453   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6454 
6455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6456 @*/
6457 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6458 {
6459   PetscFunctionBeginHot;
6460   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6461   PetscFunctionReturn(PETSC_SUCCESS);
6462 }
6463 
6464 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6465 {
6466   DMLabel            depthLabel;
6467   PetscSection       clSection;
6468   IS                 clPoints;
6469   PetscScalar       *array;
6470   const PetscScalar *vArray;
6471   PetscInt          *points = NULL;
6472   const PetscInt    *clp, *perm = NULL;
6473   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6474 
6475   PetscFunctionBeginHot;
6476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6477   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6478   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6479   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6480   PetscCall(DMPlexGetDepth(dm, &mdepth));
6481   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6482   PetscCall(PetscSectionGetNumFields(section, &numFields));
6483   if (mdepth == 1 && numFields < 2) {
6484     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6485     PetscFunctionReturn(PETSC_SUCCESS);
6486   }
6487   /* Get points */
6488   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6489   for (clsize = 0, p = 0; p < Np; p++) {
6490     PetscInt dof;
6491     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6492     clsize += dof;
6493   }
6494   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6495   /* Filter points */
6496   for (p = 0; p < numPoints * 2; p += 2) {
6497     PetscInt dep;
6498 
6499     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6500     if (dep != depth) continue;
6501     points[Np * 2 + 0] = points[p];
6502     points[Np * 2 + 1] = points[p + 1];
6503     ++Np;
6504   }
6505   /* Get array */
6506   if (!values || !*values) {
6507     PetscInt asize = 0, dof;
6508 
6509     for (p = 0; p < Np * 2; p += 2) {
6510       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6511       asize += dof;
6512     }
6513     if (!values) {
6514       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6515       if (csize) *csize = asize;
6516       PetscFunctionReturn(PETSC_SUCCESS);
6517     }
6518     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6519   } else {
6520     array = *values;
6521   }
6522   PetscCall(VecGetArrayRead(v, &vArray));
6523   /* Get values */
6524   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6525   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6526   /* Cleanup points */
6527   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6528   /* Cleanup array */
6529   PetscCall(VecRestoreArrayRead(v, &vArray));
6530   if (!*values) {
6531     if (csize) *csize = size;
6532     *values = array;
6533   } else {
6534     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6535     *csize = size;
6536   }
6537   PetscFunctionReturn(PETSC_SUCCESS);
6538 }
6539 
6540 /*@C
6541   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6542 
6543   Not collective
6544 
6545   Input Parameters:
6546 + dm      - The `DM`
6547 . section - The section describing the layout in `v`, or `NULL` to use the default section
6548 . v       - The local vector
6549 . point   - The point in the `DM`
6550 . csize   - The number of values in the closure, or `NULL`
6551 - values  - The array of values, which is a borrowed array and should not be freed
6552 
6553   Level: intermediate
6554 
6555   Note:
6556   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6557 
6558   Fortran Notes:
6559   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6560 
6561 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6562 @*/
6563 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6564 {
6565   PetscInt size = 0;
6566 
6567   PetscFunctionBegin;
6568   /* Should work without recalculating size */
6569   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6570   *values = NULL;
6571   PetscFunctionReturn(PETSC_SUCCESS);
6572 }
6573 
6574 static inline void add(PetscScalar *x, PetscScalar y)
6575 {
6576   *x += y;
6577 }
6578 static inline void insert(PetscScalar *x, PetscScalar y)
6579 {
6580   *x = y;
6581 }
6582 
6583 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[])
6584 {
6585   PetscInt        cdof;  /* The number of constraints on this point */
6586   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6587   PetscScalar    *a;
6588   PetscInt        off, cind = 0, k;
6589 
6590   PetscFunctionBegin;
6591   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6592   PetscCall(PetscSectionGetOffset(section, point, &off));
6593   a = &array[off];
6594   if (!cdof || setBC) {
6595     if (clperm) {
6596       if (perm) {
6597         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6598       } else {
6599         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6600       }
6601     } else {
6602       if (perm) {
6603         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6604       } else {
6605         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6606       }
6607     }
6608   } else {
6609     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6610     if (clperm) {
6611       if (perm) {
6612         for (k = 0; k < dof; ++k) {
6613           if ((cind < cdof) && (k == cdofs[cind])) {
6614             ++cind;
6615             continue;
6616           }
6617           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6618         }
6619       } else {
6620         for (k = 0; k < dof; ++k) {
6621           if ((cind < cdof) && (k == cdofs[cind])) {
6622             ++cind;
6623             continue;
6624           }
6625           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6626         }
6627       }
6628     } else {
6629       if (perm) {
6630         for (k = 0; k < dof; ++k) {
6631           if ((cind < cdof) && (k == cdofs[cind])) {
6632             ++cind;
6633             continue;
6634           }
6635           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6636         }
6637       } else {
6638         for (k = 0; k < dof; ++k) {
6639           if ((cind < cdof) && (k == cdofs[cind])) {
6640             ++cind;
6641             continue;
6642           }
6643           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6644         }
6645       }
6646     }
6647   }
6648   PetscFunctionReturn(PETSC_SUCCESS);
6649 }
6650 
6651 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[])
6652 {
6653   PetscInt        cdof;  /* The number of constraints on this point */
6654   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6655   PetscScalar    *a;
6656   PetscInt        off, cind = 0, k;
6657 
6658   PetscFunctionBegin;
6659   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6660   PetscCall(PetscSectionGetOffset(section, point, &off));
6661   a = &array[off];
6662   if (cdof) {
6663     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6664     if (clperm) {
6665       if (perm) {
6666         for (k = 0; k < dof; ++k) {
6667           if ((cind < cdof) && (k == cdofs[cind])) {
6668             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6669             cind++;
6670           }
6671         }
6672       } else {
6673         for (k = 0; k < dof; ++k) {
6674           if ((cind < cdof) && (k == cdofs[cind])) {
6675             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6676             cind++;
6677           }
6678         }
6679       }
6680     } else {
6681       if (perm) {
6682         for (k = 0; k < dof; ++k) {
6683           if ((cind < cdof) && (k == cdofs[cind])) {
6684             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6685             cind++;
6686           }
6687         }
6688       } else {
6689         for (k = 0; k < dof; ++k) {
6690           if ((cind < cdof) && (k == cdofs[cind])) {
6691             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6692             cind++;
6693           }
6694         }
6695       }
6696     }
6697   }
6698   PetscFunctionReturn(PETSC_SUCCESS);
6699 }
6700 
6701 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[])
6702 {
6703   PetscScalar    *a;
6704   PetscInt        fdof, foff, fcdof, foffset = *offset;
6705   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6706   PetscInt        cind = 0, b;
6707 
6708   PetscFunctionBegin;
6709   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6710   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6711   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6712   a = &array[foff];
6713   if (!fcdof || setBC) {
6714     if (clperm) {
6715       if (perm) {
6716         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6717       } else {
6718         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6719       }
6720     } else {
6721       if (perm) {
6722         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6723       } else {
6724         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6725       }
6726     }
6727   } else {
6728     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6729     if (clperm) {
6730       if (perm) {
6731         for (b = 0; b < fdof; b++) {
6732           if ((cind < fcdof) && (b == fcdofs[cind])) {
6733             ++cind;
6734             continue;
6735           }
6736           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6737         }
6738       } else {
6739         for (b = 0; b < fdof; b++) {
6740           if ((cind < fcdof) && (b == fcdofs[cind])) {
6741             ++cind;
6742             continue;
6743           }
6744           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6745         }
6746       }
6747     } else {
6748       if (perm) {
6749         for (b = 0; b < fdof; b++) {
6750           if ((cind < fcdof) && (b == fcdofs[cind])) {
6751             ++cind;
6752             continue;
6753           }
6754           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6755         }
6756       } else {
6757         for (b = 0; b < fdof; b++) {
6758           if ((cind < fcdof) && (b == fcdofs[cind])) {
6759             ++cind;
6760             continue;
6761           }
6762           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6763         }
6764       }
6765     }
6766   }
6767   *offset += fdof;
6768   PetscFunctionReturn(PETSC_SUCCESS);
6769 }
6770 
6771 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[])
6772 {
6773   PetscScalar    *a;
6774   PetscInt        fdof, foff, fcdof, foffset = *offset;
6775   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6776   PetscInt        Nc, cind = 0, ncind = 0, b;
6777   PetscBool       ncSet, fcSet;
6778 
6779   PetscFunctionBegin;
6780   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6781   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6782   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6783   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6784   a = &array[foff];
6785   if (fcdof) {
6786     /* We just override fcdof and fcdofs with Ncc and comps */
6787     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6788     if (clperm) {
6789       if (perm) {
6790         if (comps) {
6791           for (b = 0; b < fdof; b++) {
6792             ncSet = fcSet = PETSC_FALSE;
6793             if (b % Nc == comps[ncind]) {
6794               ncind = (ncind + 1) % Ncc;
6795               ncSet = PETSC_TRUE;
6796             }
6797             if ((cind < fcdof) && (b == fcdofs[cind])) {
6798               ++cind;
6799               fcSet = PETSC_TRUE;
6800             }
6801             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6802           }
6803         } else {
6804           for (b = 0; b < fdof; b++) {
6805             if ((cind < fcdof) && (b == fcdofs[cind])) {
6806               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6807               ++cind;
6808             }
6809           }
6810         }
6811       } else {
6812         if (comps) {
6813           for (b = 0; b < fdof; b++) {
6814             ncSet = fcSet = PETSC_FALSE;
6815             if (b % Nc == comps[ncind]) {
6816               ncind = (ncind + 1) % Ncc;
6817               ncSet = PETSC_TRUE;
6818             }
6819             if ((cind < fcdof) && (b == fcdofs[cind])) {
6820               ++cind;
6821               fcSet = PETSC_TRUE;
6822             }
6823             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6824           }
6825         } else {
6826           for (b = 0; b < fdof; b++) {
6827             if ((cind < fcdof) && (b == fcdofs[cind])) {
6828               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6829               ++cind;
6830             }
6831           }
6832         }
6833       }
6834     } else {
6835       if (perm) {
6836         if (comps) {
6837           for (b = 0; b < fdof; b++) {
6838             ncSet = fcSet = PETSC_FALSE;
6839             if (b % Nc == comps[ncind]) {
6840               ncind = (ncind + 1) % Ncc;
6841               ncSet = PETSC_TRUE;
6842             }
6843             if ((cind < fcdof) && (b == fcdofs[cind])) {
6844               ++cind;
6845               fcSet = PETSC_TRUE;
6846             }
6847             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6848           }
6849         } else {
6850           for (b = 0; b < fdof; b++) {
6851             if ((cind < fcdof) && (b == fcdofs[cind])) {
6852               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6853               ++cind;
6854             }
6855           }
6856         }
6857       } else {
6858         if (comps) {
6859           for (b = 0; b < fdof; b++) {
6860             ncSet = fcSet = PETSC_FALSE;
6861             if (b % Nc == comps[ncind]) {
6862               ncind = (ncind + 1) % Ncc;
6863               ncSet = PETSC_TRUE;
6864             }
6865             if ((cind < fcdof) && (b == fcdofs[cind])) {
6866               ++cind;
6867               fcSet = PETSC_TRUE;
6868             }
6869             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6870           }
6871         } else {
6872           for (b = 0; b < fdof; b++) {
6873             if ((cind < fcdof) && (b == fcdofs[cind])) {
6874               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6875               ++cind;
6876             }
6877           }
6878         }
6879       }
6880     }
6881   }
6882   *offset += fdof;
6883   PetscFunctionReturn(PETSC_SUCCESS);
6884 }
6885 
6886 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6887 {
6888   PetscScalar    *array;
6889   const PetscInt *cone, *coneO;
6890   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6891 
6892   PetscFunctionBeginHot;
6893   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6894   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6895   PetscCall(DMPlexGetCone(dm, point, &cone));
6896   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6897   PetscCall(VecGetArray(v, &array));
6898   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6899     const PetscInt cp = !p ? point : cone[p - 1];
6900     const PetscInt o  = !p ? 0 : coneO[p - 1];
6901 
6902     if ((cp < pStart) || (cp >= pEnd)) {
6903       dof = 0;
6904       continue;
6905     }
6906     PetscCall(PetscSectionGetDof(section, cp, &dof));
6907     /* ADD_VALUES */
6908     {
6909       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6910       PetscScalar    *a;
6911       PetscInt        cdof, coff, cind = 0, k;
6912 
6913       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6914       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6915       a = &array[coff];
6916       if (!cdof) {
6917         if (o >= 0) {
6918           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6919         } else {
6920           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6921         }
6922       } else {
6923         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6924         if (o >= 0) {
6925           for (k = 0; k < dof; ++k) {
6926             if ((cind < cdof) && (k == cdofs[cind])) {
6927               ++cind;
6928               continue;
6929             }
6930             a[k] += values[off + k];
6931           }
6932         } else {
6933           for (k = 0; k < dof; ++k) {
6934             if ((cind < cdof) && (k == cdofs[cind])) {
6935               ++cind;
6936               continue;
6937             }
6938             a[k] += values[off + dof - k - 1];
6939           }
6940         }
6941       }
6942     }
6943   }
6944   PetscCall(VecRestoreArray(v, &array));
6945   PetscFunctionReturn(PETSC_SUCCESS);
6946 }
6947 
6948 /*@C
6949   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6950 
6951   Not collective
6952 
6953   Input Parameters:
6954 + dm      - The `DM`
6955 . section - The section describing the layout in `v`, or `NULL` to use the default section
6956 . v       - The local vector
6957 . point   - The point in the `DM`
6958 . values  - The array of values
6959 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6960          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6961 
6962   Level: intermediate
6963 
6964 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6965 @*/
6966 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6967 {
6968   PetscSection    clSection;
6969   IS              clPoints;
6970   PetscScalar    *array;
6971   PetscInt       *points = NULL;
6972   const PetscInt *clp, *clperm = NULL;
6973   PetscInt        depth, numFields, numPoints, p, clsize;
6974 
6975   PetscFunctionBeginHot;
6976   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6977   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6978   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6979   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6980   PetscCall(DMPlexGetDepth(dm, &depth));
6981   PetscCall(PetscSectionGetNumFields(section, &numFields));
6982   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6983     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6984     PetscFunctionReturn(PETSC_SUCCESS);
6985   }
6986   /* Get points */
6987   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6988   for (clsize = 0, p = 0; p < numPoints; p++) {
6989     PetscInt dof;
6990     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6991     clsize += dof;
6992   }
6993   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6994   /* Get array */
6995   PetscCall(VecGetArray(v, &array));
6996   /* Get values */
6997   if (numFields > 0) {
6998     PetscInt offset = 0, f;
6999     for (f = 0; f < numFields; ++f) {
7000       const PetscInt    **perms = NULL;
7001       const PetscScalar **flips = NULL;
7002 
7003       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7004       switch (mode) {
7005       case INSERT_VALUES:
7006         for (p = 0; p < numPoints; p++) {
7007           const PetscInt     point = points[2 * p];
7008           const PetscInt    *perm  = perms ? perms[p] : NULL;
7009           const PetscScalar *flip  = flips ? flips[p] : NULL;
7010           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7011         }
7012         break;
7013       case INSERT_ALL_VALUES:
7014         for (p = 0; p < numPoints; p++) {
7015           const PetscInt     point = points[2 * p];
7016           const PetscInt    *perm  = perms ? perms[p] : NULL;
7017           const PetscScalar *flip  = flips ? flips[p] : NULL;
7018           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7019         }
7020         break;
7021       case INSERT_BC_VALUES:
7022         for (p = 0; p < numPoints; p++) {
7023           const PetscInt     point = points[2 * p];
7024           const PetscInt    *perm  = perms ? perms[p] : NULL;
7025           const PetscScalar *flip  = flips ? flips[p] : NULL;
7026           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7027         }
7028         break;
7029       case ADD_VALUES:
7030         for (p = 0; p < numPoints; p++) {
7031           const PetscInt     point = points[2 * p];
7032           const PetscInt    *perm  = perms ? perms[p] : NULL;
7033           const PetscScalar *flip  = flips ? flips[p] : NULL;
7034           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7035         }
7036         break;
7037       case ADD_ALL_VALUES:
7038         for (p = 0; p < numPoints; p++) {
7039           const PetscInt     point = points[2 * p];
7040           const PetscInt    *perm  = perms ? perms[p] : NULL;
7041           const PetscScalar *flip  = flips ? flips[p] : NULL;
7042           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7043         }
7044         break;
7045       case ADD_BC_VALUES:
7046         for (p = 0; p < numPoints; p++) {
7047           const PetscInt     point = points[2 * p];
7048           const PetscInt    *perm  = perms ? perms[p] : NULL;
7049           const PetscScalar *flip  = flips ? flips[p] : NULL;
7050           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7051         }
7052         break;
7053       default:
7054         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7055       }
7056       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7057     }
7058   } else {
7059     PetscInt            dof, off;
7060     const PetscInt    **perms = NULL;
7061     const PetscScalar **flips = NULL;
7062 
7063     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7064     switch (mode) {
7065     case INSERT_VALUES:
7066       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7067         const PetscInt     point = points[2 * p];
7068         const PetscInt    *perm  = perms ? perms[p] : NULL;
7069         const PetscScalar *flip  = flips ? flips[p] : NULL;
7070         PetscCall(PetscSectionGetDof(section, point, &dof));
7071         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7072       }
7073       break;
7074     case INSERT_ALL_VALUES:
7075       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7076         const PetscInt     point = points[2 * p];
7077         const PetscInt    *perm  = perms ? perms[p] : NULL;
7078         const PetscScalar *flip  = flips ? flips[p] : NULL;
7079         PetscCall(PetscSectionGetDof(section, point, &dof));
7080         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7081       }
7082       break;
7083     case INSERT_BC_VALUES:
7084       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7085         const PetscInt     point = points[2 * p];
7086         const PetscInt    *perm  = perms ? perms[p] : NULL;
7087         const PetscScalar *flip  = flips ? flips[p] : NULL;
7088         PetscCall(PetscSectionGetDof(section, point, &dof));
7089         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7090       }
7091       break;
7092     case ADD_VALUES:
7093       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7094         const PetscInt     point = points[2 * p];
7095         const PetscInt    *perm  = perms ? perms[p] : NULL;
7096         const PetscScalar *flip  = flips ? flips[p] : NULL;
7097         PetscCall(PetscSectionGetDof(section, point, &dof));
7098         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7099       }
7100       break;
7101     case ADD_ALL_VALUES:
7102       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7103         const PetscInt     point = points[2 * p];
7104         const PetscInt    *perm  = perms ? perms[p] : NULL;
7105         const PetscScalar *flip  = flips ? flips[p] : NULL;
7106         PetscCall(PetscSectionGetDof(section, point, &dof));
7107         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7108       }
7109       break;
7110     case ADD_BC_VALUES:
7111       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7112         const PetscInt     point = points[2 * p];
7113         const PetscInt    *perm  = perms ? perms[p] : NULL;
7114         const PetscScalar *flip  = flips ? flips[p] : NULL;
7115         PetscCall(PetscSectionGetDof(section, point, &dof));
7116         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7117       }
7118       break;
7119     default:
7120       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7121     }
7122     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7123   }
7124   /* Cleanup points */
7125   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7126   /* Cleanup array */
7127   PetscCall(VecRestoreArray(v, &array));
7128   PetscFunctionReturn(PETSC_SUCCESS);
7129 }
7130 
7131 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7132 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7133 {
7134   PetscFunctionBegin;
7135   *contains = PETSC_TRUE;
7136   if (label) {
7137     PetscInt fdof;
7138 
7139     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7140     if (!*contains) {
7141       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7142       *offset += fdof;
7143       PetscFunctionReturn(PETSC_SUCCESS);
7144     }
7145   }
7146   PetscFunctionReturn(PETSC_SUCCESS);
7147 }
7148 
7149 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7150 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)
7151 {
7152   PetscSection    clSection;
7153   IS              clPoints;
7154   PetscScalar    *array;
7155   PetscInt       *points = NULL;
7156   const PetscInt *clp;
7157   PetscInt        numFields, numPoints, p;
7158   PetscInt        offset = 0, f;
7159 
7160   PetscFunctionBeginHot;
7161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7162   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7163   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7164   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7165   PetscCall(PetscSectionGetNumFields(section, &numFields));
7166   /* Get points */
7167   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7168   /* Get array */
7169   PetscCall(VecGetArray(v, &array));
7170   /* Get values */
7171   for (f = 0; f < numFields; ++f) {
7172     const PetscInt    **perms = NULL;
7173     const PetscScalar **flips = NULL;
7174     PetscBool           contains;
7175 
7176     if (!fieldActive[f]) {
7177       for (p = 0; p < numPoints * 2; p += 2) {
7178         PetscInt fdof;
7179         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7180         offset += fdof;
7181       }
7182       continue;
7183     }
7184     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7185     switch (mode) {
7186     case INSERT_VALUES:
7187       for (p = 0; p < numPoints; p++) {
7188         const PetscInt     point = points[2 * p];
7189         const PetscInt    *perm  = perms ? perms[p] : NULL;
7190         const PetscScalar *flip  = flips ? flips[p] : NULL;
7191         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7192         if (!contains) continue;
7193         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7194       }
7195       break;
7196     case INSERT_ALL_VALUES:
7197       for (p = 0; p < numPoints; p++) {
7198         const PetscInt     point = points[2 * p];
7199         const PetscInt    *perm  = perms ? perms[p] : NULL;
7200         const PetscScalar *flip  = flips ? flips[p] : NULL;
7201         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7202         if (!contains) continue;
7203         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7204       }
7205       break;
7206     case INSERT_BC_VALUES:
7207       for (p = 0; p < numPoints; p++) {
7208         const PetscInt     point = points[2 * p];
7209         const PetscInt    *perm  = perms ? perms[p] : NULL;
7210         const PetscScalar *flip  = flips ? flips[p] : NULL;
7211         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7212         if (!contains) continue;
7213         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7214       }
7215       break;
7216     case ADD_VALUES:
7217       for (p = 0; p < numPoints; p++) {
7218         const PetscInt     point = points[2 * p];
7219         const PetscInt    *perm  = perms ? perms[p] : NULL;
7220         const PetscScalar *flip  = flips ? flips[p] : NULL;
7221         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7222         if (!contains) continue;
7223         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7224       }
7225       break;
7226     case ADD_ALL_VALUES:
7227       for (p = 0; p < numPoints; p++) {
7228         const PetscInt     point = points[2 * p];
7229         const PetscInt    *perm  = perms ? perms[p] : NULL;
7230         const PetscScalar *flip  = flips ? flips[p] : NULL;
7231         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7232         if (!contains) continue;
7233         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7234       }
7235       break;
7236     default:
7237       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7238     }
7239     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7240   }
7241   /* Cleanup points */
7242   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7243   /* Cleanup array */
7244   PetscCall(VecRestoreArray(v, &array));
7245   PetscFunctionReturn(PETSC_SUCCESS);
7246 }
7247 
7248 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7249 {
7250   PetscMPIInt rank;
7251   PetscInt    i, j;
7252 
7253   PetscFunctionBegin;
7254   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7255   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7256   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7257   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7258   numCIndices = numCIndices ? numCIndices : numRIndices;
7259   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7260   for (i = 0; i < numRIndices; i++) {
7261     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7262     for (j = 0; j < numCIndices; j++) {
7263 #if defined(PETSC_USE_COMPLEX)
7264       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7265 #else
7266       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7267 #endif
7268     }
7269     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7270   }
7271   PetscFunctionReturn(PETSC_SUCCESS);
7272 }
7273 
7274 /*
7275   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7276 
7277   Input Parameters:
7278 + section - The section for this data layout
7279 . islocal - Is the section (and thus indices being requested) local or global?
7280 . point   - The point contributing dofs with these indices
7281 . off     - The global offset of this point
7282 . loff    - The local offset of each field
7283 . setBC   - The flag determining whether to include indices of boundary values
7284 . perm    - A permutation of the dofs on this point, or NULL
7285 - indperm - A permutation of the entire indices array, or NULL
7286 
7287   Output Parameter:
7288 . indices - Indices for dofs on this point
7289 
7290   Level: developer
7291 
7292   Note: The indices could be local or global, depending on the value of 'off'.
7293 */
7294 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7295 {
7296   PetscInt        dof;   /* The number of unknowns on this point */
7297   PetscInt        cdof;  /* The number of constraints on this point */
7298   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7299   PetscInt        cind = 0, k;
7300 
7301   PetscFunctionBegin;
7302   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7303   PetscCall(PetscSectionGetDof(section, point, &dof));
7304   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7305   if (!cdof || setBC) {
7306     for (k = 0; k < dof; ++k) {
7307       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7308       const PetscInt ind    = indperm ? indperm[preind] : preind;
7309 
7310       indices[ind] = off + k;
7311     }
7312   } else {
7313     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7314     for (k = 0; k < dof; ++k) {
7315       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7316       const PetscInt ind    = indperm ? indperm[preind] : preind;
7317 
7318       if ((cind < cdof) && (k == cdofs[cind])) {
7319         /* Insert check for returning constrained indices */
7320         indices[ind] = -(off + k + 1);
7321         ++cind;
7322       } else {
7323         indices[ind] = off + k - (islocal ? 0 : cind);
7324       }
7325     }
7326   }
7327   *loff += dof;
7328   PetscFunctionReturn(PETSC_SUCCESS);
7329 }
7330 
7331 /*
7332  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7333 
7334  Input Parameters:
7335 + section - a section (global or local)
7336 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7337 . point - point within section
7338 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7339 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7340 . setBC - identify constrained (boundary condition) points via involution.
7341 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7342 . permsoff - offset
7343 - indperm - index permutation
7344 
7345  Output Parameter:
7346 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7347 . indices - array to hold indices (as defined by section) of each dof associated with point
7348 
7349  Notes:
7350  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7351  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7352  in the local vector.
7353 
7354  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7355  significant).  It is invalid to call with a global section and setBC=true.
7356 
7357  Developer Note:
7358  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7359  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7360  offset could be obtained from the section instead of passing it explicitly as we do now.
7361 
7362  Example:
7363  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7364  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7365  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7366  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.
7367 
7368  Level: developer
7369 */
7370 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[])
7371 {
7372   PetscInt numFields, foff, f;
7373 
7374   PetscFunctionBegin;
7375   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7376   PetscCall(PetscSectionGetNumFields(section, &numFields));
7377   for (f = 0, foff = 0; f < numFields; ++f) {
7378     PetscInt        fdof, cfdof;
7379     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7380     PetscInt        cind = 0, b;
7381     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7382 
7383     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7384     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7385     if (!cfdof || setBC) {
7386       for (b = 0; b < fdof; ++b) {
7387         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7388         const PetscInt ind    = indperm ? indperm[preind] : preind;
7389 
7390         indices[ind] = off + foff + b;
7391       }
7392     } else {
7393       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7394       for (b = 0; b < fdof; ++b) {
7395         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7396         const PetscInt ind    = indperm ? indperm[preind] : preind;
7397 
7398         if ((cind < cfdof) && (b == fcdofs[cind])) {
7399           indices[ind] = -(off + foff + b + 1);
7400           ++cind;
7401         } else {
7402           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7403         }
7404       }
7405     }
7406     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7407     foffs[f] += fdof;
7408   }
7409   PetscFunctionReturn(PETSC_SUCCESS);
7410 }
7411 
7412 /*
7413   This version believes the globalSection offsets for each field, rather than just the point offset
7414 
7415  . foffs - The offset into 'indices' for each field, since it is segregated by field
7416 
7417  Notes:
7418  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7419  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7420 */
7421 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7422 {
7423   PetscInt numFields, foff, f;
7424 
7425   PetscFunctionBegin;
7426   PetscCall(PetscSectionGetNumFields(section, &numFields));
7427   for (f = 0; f < numFields; ++f) {
7428     PetscInt        fdof, cfdof;
7429     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7430     PetscInt        cind = 0, b;
7431     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7432 
7433     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7434     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7435     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7436     if (!cfdof) {
7437       for (b = 0; b < fdof; ++b) {
7438         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7439         const PetscInt ind    = indperm ? indperm[preind] : preind;
7440 
7441         indices[ind] = foff + b;
7442       }
7443     } else {
7444       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7445       for (b = 0; b < fdof; ++b) {
7446         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7447         const PetscInt ind    = indperm ? indperm[preind] : preind;
7448 
7449         if ((cind < cfdof) && (b == fcdofs[cind])) {
7450           indices[ind] = -(foff + b + 1);
7451           ++cind;
7452         } else {
7453           indices[ind] = foff + b - cind;
7454         }
7455       }
7456     }
7457     foffs[f] += fdof;
7458   }
7459   PetscFunctionReturn(PETSC_SUCCESS);
7460 }
7461 
7462 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7463 {
7464   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7465 
7466   PetscFunctionBegin;
7467   PetscCall(PetscSectionGetNumFields(section, &numFields));
7468   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7469   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7470   for (PetscInt p = 0; p < nPoints; p++) {
7471     PetscInt     b       = pnts[2 * p];
7472     PetscInt     bSecDof = 0, bOff;
7473     PetscInt     cSecDof = 0;
7474     PetscSection indices_section;
7475 
7476     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7477     if (!bSecDof) continue;
7478     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7479     indices_section = cSecDof > 0 ? cSec : section;
7480     if (numFields) {
7481       PetscInt fStart[32], fEnd[32];
7482 
7483       fStart[0] = 0;
7484       fEnd[0]   = 0;
7485       for (PetscInt f = 0; f < numFields; f++) {
7486         PetscInt fDof = 0;
7487 
7488         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7489         fStart[f + 1] = fStart[f] + fDof;
7490         fEnd[f + 1]   = fStart[f + 1];
7491       }
7492       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7493       // only apply permutations on one side
7494       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7495       for (PetscInt f = 0; f < numFields; f++) {
7496         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7497       }
7498     } else {
7499       PetscInt bEnd = 0;
7500 
7501       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7502       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7503 
7504       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7505     }
7506   }
7507   PetscFunctionReturn(PETSC_SUCCESS);
7508 }
7509 
7510 PETSC_INTERN PetscErrorCode DMPlexAnchorsGetSubMatModification(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscInt offsets[], PetscScalar *outMat[])
7511 {
7512   Mat             cMat;
7513   PetscSection    aSec, cSec;
7514   IS              aIS;
7515   PetscInt        aStart = -1, aEnd = -1;
7516   PetscInt        sStart = -1, sEnd = -1;
7517   PetscInt        cStart = -1, cEnd = -1;
7518   const PetscInt *anchors;
7519   PetscInt        numFields, f, p;
7520   PetscInt        newNumPoints = 0, newNumIndices = 0;
7521   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7522   PetscInt        oldOffsets[32];
7523   PetscInt        newOffsets[32];
7524   PetscInt        oldOffsetsCopy[32];
7525   PetscInt        newOffsetsCopy[32];
7526   PetscScalar    *modMat         = NULL;
7527   PetscBool       anyConstrained = PETSC_FALSE;
7528 
7529   PetscFunctionBegin;
7530   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7531   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7532   PetscCall(PetscSectionGetNumFields(section, &numFields));
7533 
7534   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7535   /* if there are point-to-point constraints */
7536   if (aSec) {
7537     PetscCall(PetscArrayzero(newOffsets, 32));
7538     PetscCall(PetscArrayzero(oldOffsets, 32));
7539     PetscCall(ISGetIndices(aIS, &anchors));
7540     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7541     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7542     /* figure out how many points are going to be in the new element matrix
7543      * (we allow double counting, because it's all just going to be summed
7544      * into the global matrix anyway) */
7545     for (p = 0; p < 2 * numPoints; p += 2) {
7546       PetscInt b    = points[p];
7547       PetscInt bDof = 0, bSecDof = 0;
7548 
7549       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7550       if (!bSecDof) continue;
7551 
7552       for (PetscInt f = 0; f < numFields; f++) {
7553         PetscInt fDof = 0;
7554 
7555         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7556         oldOffsets[f + 1] += fDof;
7557       }
7558       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7559       if (bDof) {
7560         /* this point is constrained */
7561         /* it is going to be replaced by its anchors */
7562         PetscInt bOff, q;
7563 
7564         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7565         for (q = 0; q < bDof; q++) {
7566           PetscInt a    = anchors[bOff + q];
7567           PetscInt aDof = 0;
7568 
7569           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7570           if (aDof) {
7571             anyConstrained = PETSC_TRUE;
7572             newNumPoints += 1;
7573           }
7574           newNumIndices += aDof;
7575           for (f = 0; f < numFields; ++f) {
7576             PetscInt fDof = 0;
7577 
7578             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7579             newOffsets[f + 1] += fDof;
7580           }
7581         }
7582       } else {
7583         /* this point is not constrained */
7584         newNumPoints++;
7585         newNumIndices += bSecDof;
7586         for (f = 0; f < numFields; ++f) {
7587           PetscInt fDof;
7588 
7589           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7590           newOffsets[f + 1] += fDof;
7591         }
7592       }
7593     }
7594   }
7595   if (!anyConstrained) {
7596     if (outNumPoints) *outNumPoints = 0;
7597     if (outNumIndices) *outNumIndices = 0;
7598     if (outPoints) *outPoints = NULL;
7599     if (outMat) *outMat = NULL;
7600     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7601     PetscFunctionReturn(PETSC_SUCCESS);
7602   }
7603 
7604   if (outNumPoints) *outNumPoints = newNumPoints;
7605   if (outNumIndices) *outNumIndices = newNumIndices;
7606 
7607   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7608   for (f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7609 
7610   if (!outPoints && !outMat) {
7611     if (offsets) {
7612       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7613     }
7614     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7615     PetscFunctionReturn(PETSC_SUCCESS);
7616   }
7617 
7618   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7619   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7620 
7621   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7622   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7623 
7624   /* output arrays */
7625   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7626   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7627 
7628   // get the new Points
7629   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7630     PetscInt b    = points[2 * p];
7631     PetscInt bDof = 0, bSecDof = 0, bOff;
7632 
7633     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7634     if (!bSecDof) continue;
7635     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7636     if (bDof) {
7637       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7638       for (PetscInt q = 0; q < bDof; q++) {
7639         PetscInt a = anchors[bOff + q], aDof = 0;
7640 
7641         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7642         if (aDof) {
7643           newPoints[2 * newP]     = a;
7644           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7645           newP++;
7646         }
7647       }
7648     } else {
7649       newPoints[2 * newP]     = b;
7650       newPoints[2 * newP + 1] = points[2 * p + 1];
7651       newP++;
7652     }
7653   }
7654 
7655   if (outMat) {
7656     PetscScalar *tmpMat;
7657     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7658     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7659 
7660     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7661     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7662     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7663     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7664 
7665     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7666     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7667 
7668     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7669     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7670 
7671     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7672     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7673     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7674     // for each field, insert the anchor modification into modMat
7675     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7676       PetscInt fStart    = oldOffsets[f];
7677       PetscInt fNewStart = newOffsets[f];
7678       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7679         PetscInt b    = points[2 * p];
7680         PetscInt bDof = 0, bSecDof = 0, bOff;
7681 
7682         if (b >= sStart && b < sEnd) {
7683           if (numFields) {
7684             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7685           } else {
7686             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7687           }
7688         }
7689         if (!bSecDof) continue;
7690         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7691         if (bDof) {
7692           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7693           for (PetscInt q = 0; q < bDof; q++, newP++) {
7694             PetscInt a = anchors[bOff + q], aDof = 0;
7695 
7696             if (a >= sStart && a < sEnd) {
7697               if (numFields) {
7698                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7699               } else {
7700                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7701               }
7702             }
7703             if (aDof) {
7704               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7705               for (PetscInt d = 0; d < bSecDof; d++) {
7706                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7707               }
7708             }
7709             oNew += aDof;
7710           }
7711         } else {
7712           // Insert the identity matrix in this block
7713           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7714           oNew += bSecDof;
7715           newP++;
7716         }
7717         o += bSecDof;
7718       }
7719     }
7720 
7721     *outMat = modMat;
7722 
7723     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7724     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7725     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7726     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7727     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7728   }
7729   PetscCall(ISRestoreIndices(aIS, &anchors));
7730 
7731   /* output */
7732   if (outPoints) {
7733     *outPoints = newPoints;
7734   } else {
7735     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7736   }
7737   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7738   PetscFunctionReturn(PETSC_SUCCESS);
7739 }
7740 
7741 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat_Internal(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt numRows, PetscInt numCols, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyRight, PetscBool multiplyLeft)
7742 {
7743   PetscScalar *modMat        = NULL;
7744   PetscInt     newNumIndices = -1;
7745 
7746   PetscFunctionBegin;
7747   /* If M is the matrix represented by values, get the matrix C such that we will add M * C (or, if multiplyLeft, C^T * M * C) into the global matrix.
7748      modMat is that matrix C */
7749   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7750   if (outNumIndices) *outNumIndices = newNumIndices;
7751   if (modMat) {
7752     const PetscScalar *newValues = values;
7753 
7754     if (multiplyRight) {
7755       PetscScalar *newNewValues = NULL;
7756       PetscBLASInt M            = newNumIndices;
7757       PetscBLASInt N            = numRows;
7758       PetscBLASInt K            = numIndices;
7759       PetscScalar  a = 1.0, b = 0.0;
7760 
7761       PetscCheck(numCols == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of columns: %" PetscInt_FMT ", expected %" PetscInt_FMT, numCols, numIndices);
7762 
7763       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7764       // row-major to column-major conversion, right multiplication becomes left multiplication
7765       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7766 
7767       numCols   = newNumIndices;
7768       newValues = newNewValues;
7769     }
7770 
7771     if (multiplyLeft) {
7772       PetscScalar *newNewValues = NULL;
7773       PetscBLASInt M            = numCols;
7774       PetscBLASInt N            = newNumIndices;
7775       PetscBLASInt K            = numIndices;
7776       PetscScalar  a = 1.0, b = 0.0;
7777 
7778       PetscCheck(numRows == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of rows: %" PetscInt_FMT ", expected %" PetscInt_FMT, numRows, numIndices);
7779 
7780       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7781       // row-major to column-major conversion, left multiplication becomes right multiplication
7782       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7783       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7784       newValues = newNewValues;
7785     }
7786     *outValues = (PetscScalar *)newValues;
7787     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7788   }
7789   PetscFunctionReturn(PETSC_SUCCESS);
7790 }
7791 
7792 PETSC_INTERN 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)
7793 {
7794   PetscFunctionBegin;
7795   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7796   PetscFunctionReturn(PETSC_SUCCESS);
7797 }
7798 
7799 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7800 {
7801   /* Closure ordering */
7802   PetscSection    clSection;
7803   IS              clPoints;
7804   const PetscInt *clp;
7805   PetscInt       *points;
7806   PetscInt        Ncl, Ni = 0;
7807 
7808   PetscFunctionBeginHot;
7809   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7810   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7811     PetscInt dof;
7812 
7813     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7814     Ni += dof;
7815   }
7816   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7817   *closureSize = Ni;
7818   PetscFunctionReturn(PETSC_SUCCESS);
7819 }
7820 
7821 static PetscErrorCode DMPlexGetClosureIndices_Internal(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numRows, PetscInt *numCols, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[], PetscBool multiplyRight, PetscBool multiplyLeft)
7822 {
7823   /* Closure ordering */
7824   PetscSection    clSection;
7825   IS              clPoints;
7826   const PetscInt *clp;
7827   PetscInt       *points;
7828   const PetscInt *clperm = NULL;
7829   /* Dof permutation and sign flips */
7830   const PetscInt    **perms[32] = {NULL};
7831   const PetscScalar **flips[32] = {NULL};
7832   PetscScalar        *valCopy   = NULL;
7833   /* Hanging node constraints */
7834   PetscInt    *pointsC = NULL;
7835   PetscScalar *valuesC = NULL;
7836   PetscInt     NclC, NiC;
7837 
7838   PetscInt *idx;
7839   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7840   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7841   PetscInt  idxStart, idxEnd;
7842   PetscInt  nRows, nCols;
7843 
7844   PetscFunctionBeginHot;
7845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7846   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7847   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7848   PetscAssertPointer(numRows, 6);
7849   PetscAssertPointer(numCols, 7);
7850   if (indices) PetscAssertPointer(indices, 8);
7851   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7852   if (values) PetscAssertPointer(values, 10);
7853   PetscCall(PetscSectionGetNumFields(section, &Nf));
7854   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7855   PetscCall(PetscArrayzero(offsets, 32));
7856   /* 1) Get points in closure */
7857   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7858   if (useClPerm) {
7859     PetscInt depth, clsize;
7860     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7861     for (clsize = 0, p = 0; p < Ncl; p++) {
7862       PetscInt dof;
7863       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7864       clsize += dof;
7865     }
7866     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7867   }
7868   /* 2) Get number of indices on these points and field offsets from section */
7869   for (p = 0; p < Ncl * 2; p += 2) {
7870     PetscInt dof, fdof;
7871 
7872     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7873     for (f = 0; f < Nf; ++f) {
7874       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7875       offsets[f + 1] += fdof;
7876     }
7877     Ni += dof;
7878   }
7879   if (*numRows == -1) *numRows = Ni;
7880   if (*numCols == -1) *numCols = Ni;
7881   nRows = *numRows;
7882   nCols = *numCols;
7883   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7884   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7885   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7886   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7887   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7888   for (f = 0; f < PetscMax(1, Nf); ++f) {
7889     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7890     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7891     /* may need to apply sign changes to the element matrix */
7892     if (values && flips[f]) {
7893       PetscInt foffset = offsets[f];
7894 
7895       for (p = 0; p < Ncl; ++p) {
7896         PetscInt           pnt  = points[2 * p], fdof;
7897         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7898 
7899         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7900         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7901         if (flip) {
7902           PetscInt i, j, k;
7903 
7904           if (!valCopy) {
7905             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7906             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7907             *values = valCopy;
7908           }
7909           for (i = 0; i < fdof; ++i) {
7910             PetscScalar fval = flip[i];
7911 
7912             if (multiplyRight) {
7913               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
7914             }
7915             if (multiplyLeft) {
7916               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
7917             }
7918           }
7919         }
7920         foffset += fdof;
7921       }
7922     }
7923   }
7924   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7925   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
7926   if (NclC) {
7927     if (multiplyRight) { *numCols = nCols = NiC; }
7928     if (multiplyLeft) { *numRows = nRows = NiC; }
7929     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7930     for (f = 0; f < PetscMax(1, Nf); ++f) {
7931       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7932       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7933     }
7934     for (f = 0; f < PetscMax(1, Nf); ++f) {
7935       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7936       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7937     }
7938     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7939     Ncl    = NclC;
7940     Ni     = NiC;
7941     points = pointsC;
7942     if (values) *values = valuesC;
7943   }
7944   /* 5) Calculate indices */
7945   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7946   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
7947   if (Nf) {
7948     PetscInt  idxOff;
7949     PetscBool useFieldOffsets;
7950 
7951     if (outOffsets) {
7952       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7953     }
7954     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7955     if (useFieldOffsets) {
7956       for (p = 0; p < Ncl; ++p) {
7957         const PetscInt pnt = points[p * 2];
7958 
7959         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7960       }
7961     } else {
7962       for (p = 0; p < Ncl; ++p) {
7963         const PetscInt pnt = points[p * 2];
7964 
7965         if (pnt < idxStart || pnt >= idxEnd) continue;
7966         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7967         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7968          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7969          * global section. */
7970         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7971       }
7972     }
7973   } else {
7974     PetscInt off = 0, idxOff;
7975 
7976     for (p = 0; p < Ncl; ++p) {
7977       const PetscInt  pnt  = points[p * 2];
7978       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7979 
7980       if (pnt < idxStart || pnt >= idxEnd) continue;
7981       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7982       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7983        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7984       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7985     }
7986   }
7987   /* 6) Cleanup */
7988   for (f = 0; f < PetscMax(1, Nf); ++f) {
7989     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7990     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7991   }
7992   if (NclC) {
7993     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7994   } else {
7995     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7996   }
7997 
7998   if (indices) *indices = idx;
7999   PetscFunctionReturn(PETSC_SUCCESS);
8000 }
8001 
8002 /*@C
8003   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8004 
8005   Not collective
8006 
8007   Input Parameters:
8008 + dm         - The `DM`
8009 . section    - The `PetscSection` describing the points (a local section)
8010 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8011 . point      - The point defining the closure
8012 - useClPerm  - Use the closure point permutation if available
8013 
8014   Output Parameters:
8015 + numIndices - The number of dof indices in the closure of point with the input sections
8016 . indices    - The dof indices
8017 . outOffsets - Array to write the field offsets into, or `NULL`
8018 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8019 
8020   Level: advanced
8021 
8022   Notes:
8023   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
8024 
8025   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8026   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8027   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8028   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8029   indices (with the above semantics) are implied.
8030 
8031 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8032           `PetscSection`, `DMGetGlobalSection()`
8033 @*/
8034 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8035 {
8036   PetscInt numRows = -1, numCols = -1;
8037 
8038   PetscFunctionBeginHot;
8039   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8040   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8041   *numIndices = numRows;
8042   PetscFunctionReturn(PETSC_SUCCESS);
8043 }
8044 
8045 /*@C
8046   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8047 
8048   Not collective
8049 
8050   Input Parameters:
8051 + dm         - The `DM`
8052 . section    - The `PetscSection` describing the points (a local section)
8053 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8054 . point      - The point defining the closure
8055 - useClPerm  - Use the closure point permutation if available
8056 
8057   Output Parameters:
8058 + numIndices - The number of dof indices in the closure of point with the input sections
8059 . indices    - The dof indices
8060 . outOffsets - Array to write the field offsets into, or `NULL`
8061 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8062 
8063   Level: advanced
8064 
8065   Notes:
8066   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8067 
8068   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8069   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8070   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8071   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8072   indices (with the above semantics) are implied.
8073 
8074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8075 @*/
8076 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8077 {
8078   PetscFunctionBegin;
8079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8080   PetscAssertPointer(indices, 7);
8081   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8082   PetscFunctionReturn(PETSC_SUCCESS);
8083 }
8084 
8085 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8086 {
8087   DM_Plex           *mesh = (DM_Plex *)dm->data;
8088   PetscInt          *indices;
8089   PetscInt           numIndices;
8090   const PetscScalar *valuesOrig = values;
8091   PetscErrorCode     ierr;
8092 
8093   PetscFunctionBegin;
8094   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8095   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8096   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8097   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8098   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8099   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8100 
8101   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8102 
8103   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8104   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8105   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8106   if (ierr) {
8107     PetscMPIInt rank;
8108 
8109     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8110     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8111     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8112     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8113     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8114     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8115   }
8116   if (mesh->printFEM > 1) {
8117     PetscInt i;
8118     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8119     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8120     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8121   }
8122 
8123   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8124   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8125   PetscFunctionReturn(PETSC_SUCCESS);
8126 }
8127 
8128 /*@C
8129   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8130 
8131   Not collective
8132 
8133   Input Parameters:
8134 + dm            - The `DM`
8135 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8136 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8137 . A             - The matrix
8138 . point         - The point in the `DM`
8139 . values        - The array of values
8140 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8141 
8142   Level: intermediate
8143 
8144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8145 @*/
8146 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8147 {
8148   PetscFunctionBegin;
8149   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8150   PetscFunctionReturn(PETSC_SUCCESS);
8151 }
8152 
8153 /*@C
8154   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8155 
8156   Not collective
8157 
8158   Input Parameters:
8159 + dmRow            - The `DM` for the row fields
8160 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8161 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8162 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8163 . dmCol            - The `DM` for the column fields
8164 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8165 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8166 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8167 . A                - The matrix
8168 . point            - The point in the `DM`
8169 . values           - The array of values
8170 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8171 
8172   Level: intermediate
8173 
8174 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8175 @*/
8176 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, PetscBool useRowPerm, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, PetscBool useColPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8177 {
8178   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8179   PetscInt          *indicesRow, *indicesCol;
8180   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8181   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8182 
8183   PetscErrorCode ierr;
8184 
8185   PetscFunctionBegin;
8186   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8187   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8188   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8189   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8190   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8191   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8192   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8193   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8194   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8195   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8196   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8197 
8198   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8199   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8200   valuesV1 = valuesV0;
8201   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8202   valuesV2 = valuesV1;
8203   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8204 
8205   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8206   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8207   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8208   if (ierr) {
8209     PetscMPIInt rank;
8210 
8211     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8212     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8213     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8214     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8215     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8216     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8217     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8218   }
8219 
8220   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8221   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8222   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8223   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8224   PetscFunctionReturn(PETSC_SUCCESS);
8225 }
8226 
8227 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8228 {
8229   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8230   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8231   PetscInt       *cpoints = NULL;
8232   PetscInt       *findices, *cindices;
8233   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8234   PetscInt        foffsets[32], coffsets[32];
8235   DMPolytopeType  ct;
8236   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8237   PetscErrorCode  ierr;
8238 
8239   PetscFunctionBegin;
8240   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8241   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8242   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8243   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8244   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8245   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8246   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8247   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8248   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8249   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8250   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8251   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8252   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8253   PetscCall(PetscArrayzero(foffsets, 32));
8254   PetscCall(PetscArrayzero(coffsets, 32));
8255   /* Column indices */
8256   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8257   maxFPoints = numCPoints;
8258   /* Compress out points not in the section */
8259   /*   TODO: Squeeze out points with 0 dof as well */
8260   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8261   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8262     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8263       cpoints[q * 2]     = cpoints[p];
8264       cpoints[q * 2 + 1] = cpoints[p + 1];
8265       ++q;
8266     }
8267   }
8268   numCPoints = q;
8269   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8270     PetscInt fdof;
8271 
8272     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8273     if (!dof) continue;
8274     for (f = 0; f < numFields; ++f) {
8275       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8276       coffsets[f + 1] += fdof;
8277     }
8278     numCIndices += dof;
8279   }
8280   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8281   /* Row indices */
8282   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8283   {
8284     DMPlexTransform tr;
8285     DMPolytopeType *rct;
8286     PetscInt       *rsize, *rcone, *rornt, Nt;
8287 
8288     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8289     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8290     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8291     numSubcells = rsize[Nt - 1];
8292     PetscCall(DMPlexTransformDestroy(&tr));
8293   }
8294   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8295   for (r = 0, q = 0; r < numSubcells; ++r) {
8296     /* TODO Map from coarse to fine cells */
8297     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8298     /* Compress out points not in the section */
8299     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8300     for (p = 0; p < numFPoints * 2; p += 2) {
8301       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8302         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8303         if (!dof) continue;
8304         for (s = 0; s < q; ++s)
8305           if (fpoints[p] == ftotpoints[s * 2]) break;
8306         if (s < q) continue;
8307         ftotpoints[q * 2]     = fpoints[p];
8308         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8309         ++q;
8310       }
8311     }
8312     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8313   }
8314   numFPoints = q;
8315   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8316     PetscInt fdof;
8317 
8318     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8319     if (!dof) continue;
8320     for (f = 0; f < numFields; ++f) {
8321       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8322       foffsets[f + 1] += fdof;
8323     }
8324     numFIndices += dof;
8325   }
8326   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8327 
8328   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8329   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8330   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8331   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8332   if (numFields) {
8333     const PetscInt **permsF[32] = {NULL};
8334     const PetscInt **permsC[32] = {NULL};
8335 
8336     for (f = 0; f < numFields; f++) {
8337       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8338       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8339     }
8340     for (p = 0; p < numFPoints; p++) {
8341       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8342       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8343     }
8344     for (p = 0; p < numCPoints; p++) {
8345       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8346       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8347     }
8348     for (f = 0; f < numFields; f++) {
8349       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8350       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8351     }
8352   } else {
8353     const PetscInt **permsF = NULL;
8354     const PetscInt **permsC = NULL;
8355 
8356     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8357     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8358     for (p = 0, off = 0; p < numFPoints; p++) {
8359       const PetscInt *perm = permsF ? permsF[p] : NULL;
8360 
8361       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8362       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8363     }
8364     for (p = 0, off = 0; p < numCPoints; p++) {
8365       const PetscInt *perm = permsC ? permsC[p] : NULL;
8366 
8367       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8368       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8369     }
8370     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8371     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8372   }
8373   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8374   /* TODO: flips */
8375   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8376   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8377   if (ierr) {
8378     PetscMPIInt rank;
8379 
8380     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8381     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8382     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8383     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8384     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8385   }
8386   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8387   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8388   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8389   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8390   PetscFunctionReturn(PETSC_SUCCESS);
8391 }
8392 
8393 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8394 {
8395   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8396   PetscInt       *cpoints      = NULL;
8397   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8398   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8399   DMPolytopeType  ct;
8400   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8401 
8402   PetscFunctionBegin;
8403   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8404   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8405   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8406   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8407   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8408   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8409   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8410   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8411   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8412   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8413   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8414   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8415   /* Column indices */
8416   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8417   maxFPoints = numCPoints;
8418   /* Compress out points not in the section */
8419   /*   TODO: Squeeze out points with 0 dof as well */
8420   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8421   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8422     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8423       cpoints[q * 2]     = cpoints[p];
8424       cpoints[q * 2 + 1] = cpoints[p + 1];
8425       ++q;
8426     }
8427   }
8428   numCPoints = q;
8429   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8430     PetscInt fdof;
8431 
8432     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8433     if (!dof) continue;
8434     for (f = 0; f < numFields; ++f) {
8435       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8436       coffsets[f + 1] += fdof;
8437     }
8438     numCIndices += dof;
8439   }
8440   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8441   /* Row indices */
8442   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8443   {
8444     DMPlexTransform tr;
8445     DMPolytopeType *rct;
8446     PetscInt       *rsize, *rcone, *rornt, Nt;
8447 
8448     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8449     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8450     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8451     numSubcells = rsize[Nt - 1];
8452     PetscCall(DMPlexTransformDestroy(&tr));
8453   }
8454   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8455   for (r = 0, q = 0; r < numSubcells; ++r) {
8456     /* TODO Map from coarse to fine cells */
8457     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8458     /* Compress out points not in the section */
8459     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8460     for (p = 0; p < numFPoints * 2; p += 2) {
8461       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8462         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8463         if (!dof) continue;
8464         for (s = 0; s < q; ++s)
8465           if (fpoints[p] == ftotpoints[s * 2]) break;
8466         if (s < q) continue;
8467         ftotpoints[q * 2]     = fpoints[p];
8468         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8469         ++q;
8470       }
8471     }
8472     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8473   }
8474   numFPoints = q;
8475   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8476     PetscInt fdof;
8477 
8478     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8479     if (!dof) continue;
8480     for (f = 0; f < numFields; ++f) {
8481       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8482       foffsets[f + 1] += fdof;
8483     }
8484     numFIndices += dof;
8485   }
8486   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8487 
8488   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8489   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8490   if (numFields) {
8491     const PetscInt **permsF[32] = {NULL};
8492     const PetscInt **permsC[32] = {NULL};
8493 
8494     for (f = 0; f < numFields; f++) {
8495       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8496       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8497     }
8498     for (p = 0; p < numFPoints; p++) {
8499       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8500       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8501     }
8502     for (p = 0; p < numCPoints; p++) {
8503       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8504       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8505     }
8506     for (f = 0; f < numFields; f++) {
8507       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8508       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8509     }
8510   } else {
8511     const PetscInt **permsF = NULL;
8512     const PetscInt **permsC = NULL;
8513 
8514     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8515     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8516     for (p = 0, off = 0; p < numFPoints; p++) {
8517       const PetscInt *perm = permsF ? permsF[p] : NULL;
8518 
8519       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8520       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8521     }
8522     for (p = 0, off = 0; p < numCPoints; p++) {
8523       const PetscInt *perm = permsC ? permsC[p] : NULL;
8524 
8525       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8526       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8527     }
8528     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8529     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8530   }
8531   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8532   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8533   PetscFunctionReturn(PETSC_SUCCESS);
8534 }
8535 
8536 /*@C
8537   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8538 
8539   Input Parameter:
8540 . dm - The `DMPLEX` object
8541 
8542   Output Parameter:
8543 . cellHeight - The height of a cell
8544 
8545   Level: developer
8546 
8547 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8548 @*/
8549 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8550 {
8551   DM_Plex *mesh = (DM_Plex *)dm->data;
8552 
8553   PetscFunctionBegin;
8554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8555   PetscAssertPointer(cellHeight, 2);
8556   *cellHeight = mesh->vtkCellHeight;
8557   PetscFunctionReturn(PETSC_SUCCESS);
8558 }
8559 
8560 /*@C
8561   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8562 
8563   Input Parameters:
8564 + dm         - The `DMPLEX` object
8565 - cellHeight - The height of a cell
8566 
8567   Level: developer
8568 
8569 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8570 @*/
8571 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8572 {
8573   DM_Plex *mesh = (DM_Plex *)dm->data;
8574 
8575   PetscFunctionBegin;
8576   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8577   mesh->vtkCellHeight = cellHeight;
8578   PetscFunctionReturn(PETSC_SUCCESS);
8579 }
8580 
8581 /*@
8582   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8583 
8584   Input Parameters:
8585 + dm - The `DMPLEX` object
8586 - ct - The `DMPolytopeType` of the cell
8587 
8588   Output Parameters:
8589 + start - The first cell of this type, or `NULL`
8590 - end   - The upper bound on this celltype, or `NULL`
8591 
8592   Level: advanced
8593 
8594 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8595 @*/
8596 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8597 {
8598   DM_Plex *mesh = (DM_Plex *)dm->data;
8599   DMLabel  label;
8600   PetscInt pStart, pEnd;
8601 
8602   PetscFunctionBegin;
8603   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8604   if (start) {
8605     PetscAssertPointer(start, 3);
8606     *start = 0;
8607   }
8608   if (end) {
8609     PetscAssertPointer(end, 4);
8610     *end = 0;
8611   }
8612   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8613   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8614   if (mesh->tr) {
8615     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8616   } else {
8617     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8618     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8619     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8620   }
8621   PetscFunctionReturn(PETSC_SUCCESS);
8622 }
8623 
8624 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8625 {
8626   PetscSection section, globalSection;
8627   PetscInt    *numbers, p;
8628 
8629   PetscFunctionBegin;
8630   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8631   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8632   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8633   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8634   PetscCall(PetscSectionSetUp(section));
8635   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8636   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8637   for (p = pStart; p < pEnd; ++p) {
8638     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8639     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8640     else numbers[p - pStart] += shift;
8641   }
8642   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8643   if (globalSize) {
8644     PetscLayout layout;
8645     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8646     PetscCall(PetscLayoutGetSize(layout, globalSize));
8647     PetscCall(PetscLayoutDestroy(&layout));
8648   }
8649   PetscCall(PetscSectionDestroy(&section));
8650   PetscCall(PetscSectionDestroy(&globalSection));
8651   PetscFunctionReturn(PETSC_SUCCESS);
8652 }
8653 
8654 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8655 {
8656   PetscInt cellHeight, cStart, cEnd;
8657 
8658   PetscFunctionBegin;
8659   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8660   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8661   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8662   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8663   PetscFunctionReturn(PETSC_SUCCESS);
8664 }
8665 
8666 /*@
8667   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8668 
8669   Input Parameter:
8670 . dm - The `DMPLEX` object
8671 
8672   Output Parameter:
8673 . globalCellNumbers - Global cell numbers for all cells on this process
8674 
8675   Level: developer
8676 
8677 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8678 @*/
8679 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8680 {
8681   DM_Plex *mesh = (DM_Plex *)dm->data;
8682 
8683   PetscFunctionBegin;
8684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8685   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8686   *globalCellNumbers = mesh->globalCellNumbers;
8687   PetscFunctionReturn(PETSC_SUCCESS);
8688 }
8689 
8690 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8691 {
8692   PetscInt vStart, vEnd;
8693 
8694   PetscFunctionBegin;
8695   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8696   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8697   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8698   PetscFunctionReturn(PETSC_SUCCESS);
8699 }
8700 
8701 /*@
8702   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8703 
8704   Input Parameter:
8705 . dm - The `DMPLEX` object
8706 
8707   Output Parameter:
8708 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8709 
8710   Level: developer
8711 
8712 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8713 @*/
8714 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8715 {
8716   DM_Plex *mesh = (DM_Plex *)dm->data;
8717 
8718   PetscFunctionBegin;
8719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8720   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8721   *globalVertexNumbers = mesh->globalVertexNumbers;
8722   PetscFunctionReturn(PETSC_SUCCESS);
8723 }
8724 
8725 /*@
8726   DMPlexCreatePointNumbering - Create a global numbering for all points.
8727 
8728   Collective
8729 
8730   Input Parameter:
8731 . dm - The `DMPLEX` object
8732 
8733   Output Parameter:
8734 . globalPointNumbers - Global numbers for all points on this process
8735 
8736   Level: developer
8737 
8738   Notes:
8739   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8740   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8741   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8742   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8743 
8744   The partitioned mesh is
8745   ```
8746   (2)--0--(3)--1--(4)    (1)--0--(2)
8747   ```
8748   and its global numbering is
8749   ```
8750   (3)--0--(4)--1--(5)--2--(6)
8751   ```
8752   Then the global numbering is provided as
8753   ```
8754   [0] Number of indices in set 5
8755   [0] 0 0
8756   [0] 1 1
8757   [0] 2 3
8758   [0] 3 4
8759   [0] 4 -6
8760   [1] Number of indices in set 3
8761   [1] 0 2
8762   [1] 1 5
8763   [1] 2 6
8764   ```
8765 
8766 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8767 @*/
8768 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8769 {
8770   IS        nums[4];
8771   PetscInt  depths[4], gdepths[4], starts[4];
8772   PetscInt  depth, d, shift = 0;
8773   PetscBool empty = PETSC_FALSE;
8774 
8775   PetscFunctionBegin;
8776   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8777   PetscCall(DMPlexGetDepth(dm, &depth));
8778   // For unstratified meshes use dim instead of depth
8779   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8780   // If any stratum is empty, we must mark all empty
8781   for (d = 0; d <= depth; ++d) {
8782     PetscInt end;
8783 
8784     depths[d] = depth - d;
8785     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8786     if (!(starts[d] - end)) empty = PETSC_TRUE;
8787   }
8788   if (empty)
8789     for (d = 0; d <= depth; ++d) {
8790       depths[d] = -1;
8791       starts[d] = -1;
8792     }
8793   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8794   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8795   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]);
8796   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8797   for (d = 0; d <= depth; ++d) {
8798     PetscInt pStart, pEnd, gsize;
8799 
8800     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8801     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8802     shift += gsize;
8803   }
8804   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8805   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8806   PetscFunctionReturn(PETSC_SUCCESS);
8807 }
8808 
8809 /*@
8810   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8811 
8812   Input Parameter:
8813 . dm - The `DMPLEX` object
8814 
8815   Output Parameter:
8816 . ranks - The rank field
8817 
8818   Options Database Key:
8819 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8820 
8821   Level: intermediate
8822 
8823 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8824 @*/
8825 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8826 {
8827   DM             rdm;
8828   PetscFE        fe;
8829   PetscScalar   *r;
8830   PetscMPIInt    rank;
8831   DMPolytopeType ct;
8832   PetscInt       dim, cStart, cEnd, c;
8833   PetscBool      simplex;
8834 
8835   PetscFunctionBeginUser;
8836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8837   PetscAssertPointer(ranks, 2);
8838   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8839   PetscCall(DMClone(dm, &rdm));
8840   PetscCall(DMGetDimension(rdm, &dim));
8841   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8842   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8843   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8844   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8845   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8846   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8847   PetscCall(PetscFEDestroy(&fe));
8848   PetscCall(DMCreateDS(rdm));
8849   PetscCall(DMCreateGlobalVector(rdm, ranks));
8850   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8851   PetscCall(VecGetArray(*ranks, &r));
8852   for (c = cStart; c < cEnd; ++c) {
8853     PetscScalar *lr;
8854 
8855     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8856     if (lr) *lr = rank;
8857   }
8858   PetscCall(VecRestoreArray(*ranks, &r));
8859   PetscCall(DMDestroy(&rdm));
8860   PetscFunctionReturn(PETSC_SUCCESS);
8861 }
8862 
8863 /*@
8864   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8865 
8866   Input Parameters:
8867 + dm    - The `DMPLEX`
8868 - label - The `DMLabel`
8869 
8870   Output Parameter:
8871 . val - The label value field
8872 
8873   Options Database Key:
8874 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8875 
8876   Level: intermediate
8877 
8878 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8879 @*/
8880 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8881 {
8882   DM             rdm, plex;
8883   Vec            lval;
8884   PetscSection   section;
8885   PetscFE        fe;
8886   PetscScalar   *v;
8887   PetscInt       dim, pStart, pEnd, p, cStart;
8888   DMPolytopeType ct;
8889   char           name[PETSC_MAX_PATH_LEN];
8890   const char    *lname, *prefix;
8891 
8892   PetscFunctionBeginUser;
8893   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8894   PetscAssertPointer(label, 2);
8895   PetscAssertPointer(val, 3);
8896   PetscCall(DMClone(dm, &rdm));
8897   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8898   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8899   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8900   PetscCall(DMDestroy(&plex));
8901   PetscCall(DMGetDimension(rdm, &dim));
8902   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8903   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8904   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8905   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8906   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8907   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8908   PetscCall(PetscFEDestroy(&fe));
8909   PetscCall(DMCreateDS(rdm));
8910   PetscCall(DMCreateGlobalVector(rdm, val));
8911   PetscCall(DMCreateLocalVector(rdm, &lval));
8912   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8913   PetscCall(DMGetLocalSection(rdm, &section));
8914   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8915   PetscCall(VecGetArray(lval, &v));
8916   for (p = pStart; p < pEnd; ++p) {
8917     PetscInt cval, dof, off;
8918 
8919     PetscCall(PetscSectionGetDof(section, p, &dof));
8920     if (!dof) continue;
8921     PetscCall(DMLabelGetValue(label, p, &cval));
8922     PetscCall(PetscSectionGetOffset(section, p, &off));
8923     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8924   }
8925   PetscCall(VecRestoreArray(lval, &v));
8926   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8927   PetscCall(VecDestroy(&lval));
8928   PetscCall(DMDestroy(&rdm));
8929   PetscFunctionReturn(PETSC_SUCCESS);
8930 }
8931 
8932 /*@
8933   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8934 
8935   Input Parameter:
8936 . dm - The `DMPLEX` object
8937 
8938   Level: developer
8939 
8940   Notes:
8941   This is a useful diagnostic when creating meshes programmatically.
8942 
8943   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8944 
8945 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8946 @*/
8947 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8948 {
8949   PetscSection    coneSection, supportSection;
8950   const PetscInt *cone, *support;
8951   PetscInt        coneSize, c, supportSize, s;
8952   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8953   PetscBool       storagecheck = PETSC_TRUE;
8954 
8955   PetscFunctionBegin;
8956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8957   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8958   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8959   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8960   /* Check that point p is found in the support of its cone points, and vice versa */
8961   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8962   for (p = pStart; p < pEnd; ++p) {
8963     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8964     PetscCall(DMPlexGetCone(dm, p, &cone));
8965     for (c = 0; c < coneSize; ++c) {
8966       PetscBool dup = PETSC_FALSE;
8967       PetscInt  d;
8968       for (d = c - 1; d >= 0; --d) {
8969         if (cone[c] == cone[d]) {
8970           dup = PETSC_TRUE;
8971           break;
8972         }
8973       }
8974       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8975       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8976       for (s = 0; s < supportSize; ++s) {
8977         if (support[s] == p) break;
8978       }
8979       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8980         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8981         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8982         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8983         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8984         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8985         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8986         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]);
8987         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8988       }
8989     }
8990     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8991     if (p != pp) {
8992       storagecheck = PETSC_FALSE;
8993       continue;
8994     }
8995     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8996     PetscCall(DMPlexGetSupport(dm, p, &support));
8997     for (s = 0; s < supportSize; ++s) {
8998       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8999       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9000       for (c = 0; c < coneSize; ++c) {
9001         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9002         if (cone[c] != pp) {
9003           c = 0;
9004           break;
9005         }
9006         if (cone[c] == p) break;
9007       }
9008       if (c >= coneSize) {
9009         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9010         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9011         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9012         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9013         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9014         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9015         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9016       }
9017     }
9018   }
9019   if (storagecheck) {
9020     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9021     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9022     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9023   }
9024   PetscFunctionReturn(PETSC_SUCCESS);
9025 }
9026 
9027 /*
9028   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.
9029 */
9030 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9031 {
9032   DMPolytopeType  cct;
9033   PetscInt        ptpoints[4];
9034   const PetscInt *cone, *ccone, *ptcone;
9035   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9036 
9037   PetscFunctionBegin;
9038   *unsplit = 0;
9039   switch (ct) {
9040   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9041     ptpoints[npt++] = c;
9042     break;
9043   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9044     PetscCall(DMPlexGetCone(dm, c, &cone));
9045     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9046     for (cp = 0; cp < coneSize; ++cp) {
9047       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9048       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9049     }
9050     break;
9051   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9052   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9053     PetscCall(DMPlexGetCone(dm, c, &cone));
9054     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9055     for (cp = 0; cp < coneSize; ++cp) {
9056       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9057       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9058       for (ccp = 0; ccp < cconeSize; ++ccp) {
9059         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9060         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9061           PetscInt p;
9062           for (p = 0; p < npt; ++p)
9063             if (ptpoints[p] == ccone[ccp]) break;
9064           if (p == npt) ptpoints[npt++] = ccone[ccp];
9065         }
9066       }
9067     }
9068     break;
9069   default:
9070     break;
9071   }
9072   for (pt = 0; pt < npt; ++pt) {
9073     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9074     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9075   }
9076   PetscFunctionReturn(PETSC_SUCCESS);
9077 }
9078 
9079 /*@
9080   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9081 
9082   Input Parameters:
9083 + dm         - The `DMPLEX` object
9084 - cellHeight - Normally 0
9085 
9086   Level: developer
9087 
9088   Notes:
9089   This is a useful diagnostic when creating meshes programmatically.
9090   Currently applicable only to homogeneous simplex or tensor meshes.
9091 
9092   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9093 
9094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9095 @*/
9096 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9097 {
9098   DMPlexInterpolatedFlag interp;
9099   DMPolytopeType         ct;
9100   PetscInt               vStart, vEnd, cStart, cEnd, c;
9101 
9102   PetscFunctionBegin;
9103   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9104   PetscCall(DMPlexIsInterpolated(dm, &interp));
9105   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9106   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9107   for (c = cStart; c < cEnd; ++c) {
9108     PetscInt *closure = NULL;
9109     PetscInt  coneSize, closureSize, cl, Nv = 0;
9110 
9111     PetscCall(DMPlexGetCellType(dm, c, &ct));
9112     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9113     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9114     if (interp == DMPLEX_INTERPOLATED_FULL) {
9115       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9116       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));
9117     }
9118     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9119     for (cl = 0; cl < closureSize * 2; cl += 2) {
9120       const PetscInt p = closure[cl];
9121       if ((p >= vStart) && (p < vEnd)) ++Nv;
9122     }
9123     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9124     /* Special Case: Tensor faces with identified vertices */
9125     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9126       PetscInt unsplit;
9127 
9128       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9129       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9130     }
9131     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));
9132   }
9133   PetscFunctionReturn(PETSC_SUCCESS);
9134 }
9135 
9136 /*@
9137   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9138 
9139   Collective
9140 
9141   Input Parameters:
9142 + dm         - The `DMPLEX` object
9143 - cellHeight - Normally 0
9144 
9145   Level: developer
9146 
9147   Notes:
9148   This is a useful diagnostic when creating meshes programmatically.
9149   This routine is only relevant for meshes that are fully interpolated across all ranks.
9150   It will error out if a partially interpolated mesh is given on some rank.
9151   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9152 
9153   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9154 
9155 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9156 @*/
9157 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9158 {
9159   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9160   DMPlexInterpolatedFlag interpEnum;
9161 
9162   PetscFunctionBegin;
9163   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9164   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9165   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9166   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9167     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9168     PetscFunctionReturn(PETSC_SUCCESS);
9169   }
9170 
9171   PetscCall(DMGetDimension(dm, &dim));
9172   PetscCall(DMPlexGetDepth(dm, &depth));
9173   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9174   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9175     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9176     for (c = cStart; c < cEnd; ++c) {
9177       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9178       const DMPolytopeType *faceTypes;
9179       DMPolytopeType        ct;
9180       PetscInt              numFaces, coneSize, f;
9181       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9182 
9183       PetscCall(DMPlexGetCellType(dm, c, &ct));
9184       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9185       if (unsplit) continue;
9186       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9187       PetscCall(DMPlexGetCone(dm, c, &cone));
9188       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9189       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9190       for (cl = 0; cl < closureSize * 2; cl += 2) {
9191         const PetscInt p = closure[cl];
9192         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9193       }
9194       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9195       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);
9196       for (f = 0; f < numFaces; ++f) {
9197         DMPolytopeType fct;
9198         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9199 
9200         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9201         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9202         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9203           const PetscInt p = fclosure[cl];
9204           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9205         }
9206         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]);
9207         for (v = 0; v < fnumCorners; ++v) {
9208           if (fclosure[v] != faces[fOff + v]) {
9209             PetscInt v1;
9210 
9211             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9212             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9213             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9214             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9215             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9216             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]);
9217           }
9218         }
9219         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9220         fOff += faceSizes[f];
9221       }
9222       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9223       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9224     }
9225   }
9226   PetscFunctionReturn(PETSC_SUCCESS);
9227 }
9228 
9229 /*@
9230   DMPlexCheckGeometry - Check the geometry of mesh cells
9231 
9232   Input Parameter:
9233 . dm - The `DMPLEX` object
9234 
9235   Level: developer
9236 
9237   Notes:
9238   This is a useful diagnostic when creating meshes programmatically.
9239 
9240   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9241 
9242 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9243 @*/
9244 PetscErrorCode DMPlexCheckGeometry(DM dm)
9245 {
9246   Vec       coordinates;
9247   PetscReal detJ, J[9], refVol = 1.0;
9248   PetscReal vol;
9249   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9250 
9251   PetscFunctionBegin;
9252   PetscCall(DMGetDimension(dm, &dim));
9253   PetscCall(DMGetCoordinateDim(dm, &dE));
9254   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9255   PetscCall(DMPlexGetDepth(dm, &depth));
9256   for (d = 0; d < dim; ++d) refVol *= 2.0;
9257   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9258   /* Make sure local coordinates are created, because that step is collective */
9259   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9260   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9261   for (c = cStart; c < cEnd; ++c) {
9262     DMPolytopeType ct;
9263     PetscInt       unsplit;
9264     PetscBool      ignoreZeroVol = PETSC_FALSE;
9265 
9266     PetscCall(DMPlexGetCellType(dm, c, &ct));
9267     switch (ct) {
9268     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9269     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9270     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9271       ignoreZeroVol = PETSC_TRUE;
9272       break;
9273     default:
9274       break;
9275     }
9276     switch (ct) {
9277     case DM_POLYTOPE_TRI_PRISM:
9278     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9279     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9280     case DM_POLYTOPE_PYRAMID:
9281       continue;
9282     default:
9283       break;
9284     }
9285     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9286     if (unsplit) continue;
9287     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9288     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);
9289     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9290     /* This should work with periodicity since DG coordinates should be used */
9291     if (depth > 1) {
9292       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9293       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);
9294       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9295     }
9296   }
9297   PetscFunctionReturn(PETSC_SUCCESS);
9298 }
9299 
9300 /*@
9301   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9302 
9303   Collective
9304 
9305   Input Parameters:
9306 + dm              - The `DMPLEX` object
9307 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9308 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9309 
9310   Level: developer
9311 
9312   Notes:
9313   This is mainly intended for debugging/testing purposes.
9314 
9315   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9316 
9317   Extra roots can come from periodic cuts, where additional points appear on the boundary
9318 
9319 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9320 @*/
9321 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9322 {
9323   PetscInt           l, nleaves, nroots, overlap;
9324   const PetscInt    *locals;
9325   const PetscSFNode *remotes;
9326   PetscBool          distributed;
9327   MPI_Comm           comm;
9328   PetscMPIInt        rank;
9329 
9330   PetscFunctionBegin;
9331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9332   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9333   else pointSF = dm->sf;
9334   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9335   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9336   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9337   {
9338     PetscMPIInt mpiFlag;
9339 
9340     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9341     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9342   }
9343   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9344   PetscCall(DMPlexIsDistributed(dm, &distributed));
9345   if (!distributed) {
9346     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);
9347     PetscFunctionReturn(PETSC_SUCCESS);
9348   }
9349   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);
9350   PetscCall(DMPlexGetOverlap(dm, &overlap));
9351 
9352   /* Check SF graph is compatible with DMPlex chart */
9353   {
9354     PetscInt pStart, pEnd, maxLeaf;
9355 
9356     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9357     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9358     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9359     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9360   }
9361 
9362   /* Check Point SF has no local points referenced */
9363   for (l = 0; l < nleaves; l++) {
9364     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);
9365   }
9366 
9367   /* Check there are no cells in interface */
9368   if (!overlap) {
9369     PetscInt cellHeight, cStart, cEnd;
9370 
9371     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9372     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9373     for (l = 0; l < nleaves; ++l) {
9374       const PetscInt point = locals ? locals[l] : l;
9375 
9376       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9377     }
9378   }
9379 
9380   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9381   {
9382     const PetscInt *rootdegree;
9383 
9384     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9385     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9386     for (l = 0; l < nleaves; ++l) {
9387       const PetscInt  point = locals ? locals[l] : l;
9388       const PetscInt *cone;
9389       PetscInt        coneSize, c, idx;
9390 
9391       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9392       PetscCall(DMPlexGetCone(dm, point, &cone));
9393       for (c = 0; c < coneSize; ++c) {
9394         if (!rootdegree[cone[c]]) {
9395           if (locals) {
9396             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9397           } else {
9398             idx = (cone[c] < nleaves) ? cone[c] : -1;
9399           }
9400           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9401         }
9402       }
9403     }
9404   }
9405   PetscFunctionReturn(PETSC_SUCCESS);
9406 }
9407 
9408 /*@
9409   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9410 
9411   Input Parameter:
9412 . dm - The `DMPLEX` object
9413 
9414   Level: developer
9415 
9416   Notes:
9417   This is a useful diagnostic when creating meshes programmatically.
9418 
9419   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9420 
9421   Currently does not include `DMPlexCheckCellShape()`.
9422 
9423 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9424 @*/
9425 PetscErrorCode DMPlexCheck(DM dm)
9426 {
9427   PetscInt cellHeight;
9428 
9429   PetscFunctionBegin;
9430   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9431   PetscCall(DMPlexCheckSymmetry(dm));
9432   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9433   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9434   PetscCall(DMPlexCheckGeometry(dm));
9435   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9436   PetscCall(DMPlexCheckInterfaceCones(dm));
9437   PetscFunctionReturn(PETSC_SUCCESS);
9438 }
9439 
9440 typedef struct cell_stats {
9441   PetscReal min, max, sum, squaresum;
9442   PetscInt  count;
9443 } cell_stats_t;
9444 
9445 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9446 {
9447   PetscInt i, N = *len;
9448 
9449   for (i = 0; i < N; i++) {
9450     cell_stats_t *A = (cell_stats_t *)a;
9451     cell_stats_t *B = (cell_stats_t *)b;
9452 
9453     B->min = PetscMin(A->min, B->min);
9454     B->max = PetscMax(A->max, B->max);
9455     B->sum += A->sum;
9456     B->squaresum += A->squaresum;
9457     B->count += A->count;
9458   }
9459 }
9460 
9461 /*@
9462   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9463 
9464   Collective
9465 
9466   Input Parameters:
9467 + dm        - The `DMPLEX` object
9468 . output    - If true, statistics will be displayed on `stdout`
9469 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9470 
9471   Level: developer
9472 
9473   Notes:
9474   This is mainly intended for debugging/testing purposes.
9475 
9476   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9477 
9478 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9479 @*/
9480 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9481 {
9482   DM           dmCoarse;
9483   cell_stats_t stats, globalStats;
9484   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9485   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9486   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9487   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9488   PetscMPIInt  rank, size;
9489 
9490   PetscFunctionBegin;
9491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9492   stats.min = PETSC_MAX_REAL;
9493   stats.max = PETSC_MIN_REAL;
9494   stats.sum = stats.squaresum = 0.;
9495   stats.count                 = 0;
9496 
9497   PetscCallMPI(MPI_Comm_size(comm, &size));
9498   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9499   PetscCall(DMGetCoordinateDim(dm, &cdim));
9500   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9501   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9502   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9503   for (c = cStart; c < cEnd; c++) {
9504     PetscInt  i;
9505     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9506 
9507     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9508     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9509     for (i = 0; i < PetscSqr(cdim); ++i) {
9510       frobJ += J[i] * J[i];
9511       frobInvJ += invJ[i] * invJ[i];
9512     }
9513     cond2 = frobJ * frobInvJ;
9514     cond  = PetscSqrtReal(cond2);
9515 
9516     stats.min = PetscMin(stats.min, cond);
9517     stats.max = PetscMax(stats.max, cond);
9518     stats.sum += cond;
9519     stats.squaresum += cond2;
9520     stats.count++;
9521     if (output && cond > limit) {
9522       PetscSection coordSection;
9523       Vec          coordsLocal;
9524       PetscScalar *coords = NULL;
9525       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9526 
9527       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9528       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9529       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9530       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9531       for (i = 0; i < Nv / cdim; ++i) {
9532         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9533         for (d = 0; d < cdim; ++d) {
9534           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9535           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9536         }
9537         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9538       }
9539       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9540       for (cl = 0; cl < clSize * 2; cl += 2) {
9541         const PetscInt edge = closure[cl];
9542 
9543         if ((edge >= eStart) && (edge < eEnd)) {
9544           PetscReal len;
9545 
9546           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9547           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9548         }
9549       }
9550       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9551       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9552     }
9553   }
9554   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9555 
9556   if (size > 1) {
9557     PetscMPIInt  blockLengths[2] = {4, 1};
9558     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9559     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9560     MPI_Op       statReduce;
9561 
9562     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9563     PetscCallMPI(MPI_Type_commit(&statType));
9564     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9565     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9566     PetscCallMPI(MPI_Op_free(&statReduce));
9567     PetscCallMPI(MPI_Type_free(&statType));
9568   } else {
9569     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9570   }
9571   if (rank == 0) {
9572     count = globalStats.count;
9573     min   = globalStats.min;
9574     max   = globalStats.max;
9575     mean  = globalStats.sum / globalStats.count;
9576     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9577   }
9578 
9579   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));
9580   PetscCall(PetscFree2(J, invJ));
9581 
9582   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9583   if (dmCoarse) {
9584     PetscBool isplex;
9585 
9586     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9587     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9588   }
9589   PetscFunctionReturn(PETSC_SUCCESS);
9590 }
9591 
9592 /*@
9593   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9594   orthogonal quality below given tolerance.
9595 
9596   Collective
9597 
9598   Input Parameters:
9599 + dm   - The `DMPLEX` object
9600 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9601 - atol - [0, 1] Absolute tolerance for tagging cells.
9602 
9603   Output Parameters:
9604 + OrthQual      - `Vec` containing orthogonal quality per cell
9605 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9606 
9607   Options Database Keys:
9608 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9609 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9610 
9611   Level: intermediate
9612 
9613   Notes:
9614   Orthogonal quality is given by the following formula\:
9615 
9616   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9617 
9618   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
9619   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9620   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9621   calculating the cosine of the angle between these vectors.
9622 
9623   Orthogonal quality ranges from 1 (best) to 0 (worst).
9624 
9625   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9626   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9627 
9628   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9629 
9630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9631 @*/
9632 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9633 {
9634   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9635   PetscInt              *idx;
9636   PetscScalar           *oqVals;
9637   const PetscScalar     *cellGeomArr, *faceGeomArr;
9638   PetscReal             *ci, *fi, *Ai;
9639   MPI_Comm               comm;
9640   Vec                    cellgeom, facegeom;
9641   DM                     dmFace, dmCell;
9642   IS                     glob;
9643   ISLocalToGlobalMapping ltog;
9644   PetscViewer            vwr;
9645 
9646   PetscFunctionBegin;
9647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9648   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9649   PetscAssertPointer(OrthQual, 4);
9650   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9651   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9652   PetscCall(DMGetDimension(dm, &nc));
9653   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9654   {
9655     DMPlexInterpolatedFlag interpFlag;
9656 
9657     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9658     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9659       PetscMPIInt rank;
9660 
9661       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9662       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9663     }
9664   }
9665   if (OrthQualLabel) {
9666     PetscAssertPointer(OrthQualLabel, 5);
9667     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9668     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9669   } else {
9670     *OrthQualLabel = NULL;
9671   }
9672   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9673   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9674   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9675   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9676   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9677   PetscCall(VecCreate(comm, OrthQual));
9678   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9679   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9680   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9681   PetscCall(VecSetUp(*OrthQual));
9682   PetscCall(ISDestroy(&glob));
9683   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9684   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9685   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9686   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9687   PetscCall(VecGetDM(cellgeom, &dmCell));
9688   PetscCall(VecGetDM(facegeom, &dmFace));
9689   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9690   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9691     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9692     PetscInt         cellarr[2], *adj = NULL;
9693     PetscScalar     *cArr, *fArr;
9694     PetscReal        minvalc = 1.0, minvalf = 1.0;
9695     PetscFVCellGeom *cg;
9696 
9697     idx[cellIter] = cell - cStart;
9698     cellarr[0]    = cell;
9699     /* Make indexing into cellGeom easier */
9700     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9701     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9702     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9703     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9704     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9705       PetscInt         i;
9706       const PetscInt   neigh  = adj[cellneigh];
9707       PetscReal        normci = 0, normfi = 0, normai = 0;
9708       PetscFVCellGeom *cgneigh;
9709       PetscFVFaceGeom *fg;
9710 
9711       /* Don't count ourselves in the neighbor list */
9712       if (neigh == cell) continue;
9713       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9714       cellarr[1] = neigh;
9715       {
9716         PetscInt        numcovpts;
9717         const PetscInt *covpts;
9718 
9719         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9720         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9721         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9722       }
9723 
9724       /* Compute c_i, f_i and their norms */
9725       for (i = 0; i < nc; i++) {
9726         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9727         fi[i] = fg->centroid[i] - cg->centroid[i];
9728         Ai[i] = fg->normal[i];
9729         normci += PetscPowReal(ci[i], 2);
9730         normfi += PetscPowReal(fi[i], 2);
9731         normai += PetscPowReal(Ai[i], 2);
9732       }
9733       normci = PetscSqrtReal(normci);
9734       normfi = PetscSqrtReal(normfi);
9735       normai = PetscSqrtReal(normai);
9736 
9737       /* Normalize and compute for each face-cell-normal pair */
9738       for (i = 0; i < nc; i++) {
9739         ci[i] = ci[i] / normci;
9740         fi[i] = fi[i] / normfi;
9741         Ai[i] = Ai[i] / normai;
9742         /* PetscAbs because I don't know if normals are guaranteed to point out */
9743         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9744         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9745       }
9746       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9747       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9748     }
9749     PetscCall(PetscFree(adj));
9750     PetscCall(PetscFree2(cArr, fArr));
9751     /* Defer to cell if they're equal */
9752     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9753     if (OrthQualLabel) {
9754       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9755     }
9756   }
9757   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9758   PetscCall(VecAssemblyBegin(*OrthQual));
9759   PetscCall(VecAssemblyEnd(*OrthQual));
9760   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9761   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9762   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9763   if (OrthQualLabel) {
9764     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9765   }
9766   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9767   PetscCall(PetscOptionsRestoreViewer(&vwr));
9768   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9769   PetscFunctionReturn(PETSC_SUCCESS);
9770 }
9771 
9772 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9773  * interpolator construction */
9774 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9775 {
9776   PetscSection section, newSection, gsection;
9777   PetscSF      sf;
9778   PetscBool    hasConstraints, ghasConstraints;
9779 
9780   PetscFunctionBegin;
9781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9782   PetscAssertPointer(odm, 2);
9783   PetscCall(DMGetLocalSection(dm, &section));
9784   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9785   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9786   if (!ghasConstraints) {
9787     PetscCall(PetscObjectReference((PetscObject)dm));
9788     *odm = dm;
9789     PetscFunctionReturn(PETSC_SUCCESS);
9790   }
9791   PetscCall(DMClone(dm, odm));
9792   PetscCall(DMCopyFields(dm, *odm));
9793   PetscCall(DMGetLocalSection(*odm, &newSection));
9794   PetscCall(DMGetPointSF(*odm, &sf));
9795   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9796   PetscCall(DMSetGlobalSection(*odm, gsection));
9797   PetscCall(PetscSectionDestroy(&gsection));
9798   PetscFunctionReturn(PETSC_SUCCESS);
9799 }
9800 
9801 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9802 {
9803   DM        dmco, dmfo;
9804   Mat       interpo;
9805   Vec       rscale;
9806   Vec       cglobalo, clocal;
9807   Vec       fglobal, fglobalo, flocal;
9808   PetscBool regular;
9809 
9810   PetscFunctionBegin;
9811   PetscCall(DMGetFullDM(dmc, &dmco));
9812   PetscCall(DMGetFullDM(dmf, &dmfo));
9813   PetscCall(DMSetCoarseDM(dmfo, dmco));
9814   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9815   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9816   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9817   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9818   PetscCall(DMCreateLocalVector(dmc, &clocal));
9819   PetscCall(VecSet(cglobalo, 0.));
9820   PetscCall(VecSet(clocal, 0.));
9821   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9822   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9823   PetscCall(DMCreateLocalVector(dmf, &flocal));
9824   PetscCall(VecSet(fglobal, 0.));
9825   PetscCall(VecSet(fglobalo, 0.));
9826   PetscCall(VecSet(flocal, 0.));
9827   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9828   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9829   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9830   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9831   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9832   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9833   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9834   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9835   *shift = fglobal;
9836   PetscCall(VecDestroy(&flocal));
9837   PetscCall(VecDestroy(&fglobalo));
9838   PetscCall(VecDestroy(&clocal));
9839   PetscCall(VecDestroy(&cglobalo));
9840   PetscCall(VecDestroy(&rscale));
9841   PetscCall(MatDestroy(&interpo));
9842   PetscCall(DMDestroy(&dmfo));
9843   PetscCall(DMDestroy(&dmco));
9844   PetscFunctionReturn(PETSC_SUCCESS);
9845 }
9846 
9847 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9848 {
9849   PetscObject shifto;
9850   Vec         shift;
9851 
9852   PetscFunctionBegin;
9853   if (!interp) {
9854     Vec rscale;
9855 
9856     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9857     PetscCall(VecDestroy(&rscale));
9858   } else {
9859     PetscCall(PetscObjectReference((PetscObject)interp));
9860   }
9861   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9862   if (!shifto) {
9863     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9864     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9865     shifto = (PetscObject)shift;
9866     PetscCall(VecDestroy(&shift));
9867   }
9868   shift = (Vec)shifto;
9869   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9870   PetscCall(VecAXPY(fineSol, 1.0, shift));
9871   PetscCall(MatDestroy(&interp));
9872   PetscFunctionReturn(PETSC_SUCCESS);
9873 }
9874 
9875 /* Pointwise interpolation
9876      Just code FEM for now
9877      u^f = I u^c
9878      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9879      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9880      I_{ij} = psi^f_i phi^c_j
9881 */
9882 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9883 {
9884   PetscSection gsc, gsf;
9885   PetscInt     m, n;
9886   void        *ctx;
9887   DM           cdm;
9888   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9889 
9890   PetscFunctionBegin;
9891   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9892   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9893   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9894   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9895 
9896   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9897   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9898   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9899   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9900   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9901 
9902   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9903   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9904   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9905   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9906   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9907   if (scaling) {
9908     /* Use naive scaling */
9909     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9910   }
9911   PetscFunctionReturn(PETSC_SUCCESS);
9912 }
9913 
9914 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9915 {
9916   VecScatter ctx;
9917 
9918   PetscFunctionBegin;
9919   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9920   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9921   PetscCall(VecScatterDestroy(&ctx));
9922   PetscFunctionReturn(PETSC_SUCCESS);
9923 }
9924 
9925 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[])
9926 {
9927   const PetscInt Nc = uOff[1] - uOff[0];
9928   PetscInt       c;
9929   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9930 }
9931 
9932 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9933 {
9934   DM           dmc;
9935   PetscDS      ds;
9936   Vec          ones, locmass;
9937   IS           cellIS;
9938   PetscFormKey key;
9939   PetscInt     depth;
9940 
9941   PetscFunctionBegin;
9942   PetscCall(DMClone(dm, &dmc));
9943   PetscCall(DMCopyDisc(dm, dmc));
9944   PetscCall(DMGetDS(dmc, &ds));
9945   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9946   PetscCall(DMCreateGlobalVector(dmc, mass));
9947   PetscCall(DMGetLocalVector(dmc, &ones));
9948   PetscCall(DMGetLocalVector(dmc, &locmass));
9949   PetscCall(DMPlexGetDepth(dmc, &depth));
9950   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9951   PetscCall(VecSet(locmass, 0.0));
9952   PetscCall(VecSet(ones, 1.0));
9953   key.label = NULL;
9954   key.value = 0;
9955   key.field = 0;
9956   key.part  = 0;
9957   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9958   PetscCall(ISDestroy(&cellIS));
9959   PetscCall(VecSet(*mass, 0.0));
9960   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9961   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9962   PetscCall(DMRestoreLocalVector(dmc, &ones));
9963   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9964   PetscCall(DMDestroy(&dmc));
9965   PetscFunctionReturn(PETSC_SUCCESS);
9966 }
9967 
9968 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9969 {
9970   PetscSection gsc, gsf;
9971   PetscInt     m, n;
9972   void        *ctx;
9973   DM           cdm;
9974   PetscBool    regular;
9975 
9976   PetscFunctionBegin;
9977   if (dmFine == dmCoarse) {
9978     DM            dmc;
9979     PetscDS       ds;
9980     PetscWeakForm wf;
9981     Vec           u;
9982     IS            cellIS;
9983     PetscFormKey  key;
9984     PetscInt      depth;
9985 
9986     PetscCall(DMClone(dmFine, &dmc));
9987     PetscCall(DMCopyDisc(dmFine, dmc));
9988     PetscCall(DMGetDS(dmc, &ds));
9989     PetscCall(PetscDSGetWeakForm(ds, &wf));
9990     PetscCall(PetscWeakFormClear(wf));
9991     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9992     PetscCall(DMCreateMatrix(dmc, mass));
9993     PetscCall(DMGetLocalVector(dmc, &u));
9994     PetscCall(DMPlexGetDepth(dmc, &depth));
9995     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9996     PetscCall(MatZeroEntries(*mass));
9997     key.label = NULL;
9998     key.value = 0;
9999     key.field = 0;
10000     key.part  = 0;
10001     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10002     PetscCall(ISDestroy(&cellIS));
10003     PetscCall(DMRestoreLocalVector(dmc, &u));
10004     PetscCall(DMDestroy(&dmc));
10005   } else {
10006     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10007     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10008     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10009     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10010 
10011     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10012     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10013     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10014     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10015 
10016     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10017     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10018     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10019     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10020   }
10021   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10022   PetscFunctionReturn(PETSC_SUCCESS);
10023 }
10024 
10025 /*@
10026   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10027 
10028   Input Parameter:
10029 . dm - The `DMPLEX` object
10030 
10031   Output Parameter:
10032 . regular - The flag
10033 
10034   Level: intermediate
10035 
10036 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10037 @*/
10038 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10039 {
10040   PetscFunctionBegin;
10041   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10042   PetscAssertPointer(regular, 2);
10043   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10044   PetscFunctionReturn(PETSC_SUCCESS);
10045 }
10046 
10047 /*@
10048   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10049 
10050   Input Parameters:
10051 + dm      - The `DMPLEX` object
10052 - regular - The flag
10053 
10054   Level: intermediate
10055 
10056 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10057 @*/
10058 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10059 {
10060   PetscFunctionBegin;
10061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10062   ((DM_Plex *)dm->data)->regularRefinement = regular;
10063   PetscFunctionReturn(PETSC_SUCCESS);
10064 }
10065 
10066 /*@
10067   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10068   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10069 
10070   Not Collective
10071 
10072   Input Parameter:
10073 . dm - The `DMPLEX` object
10074 
10075   Output Parameters:
10076 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10077 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10078 
10079   Level: intermediate
10080 
10081 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10082 @*/
10083 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10084 {
10085   DM_Plex *plex = (DM_Plex *)dm->data;
10086 
10087   PetscFunctionBegin;
10088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10089   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10090   if (anchorSection) *anchorSection = plex->anchorSection;
10091   if (anchorIS) *anchorIS = plex->anchorIS;
10092   PetscFunctionReturn(PETSC_SUCCESS);
10093 }
10094 
10095 /*@
10096   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10097 
10098   Collective
10099 
10100   Input Parameters:
10101 + dm            - The `DMPLEX` object
10102 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10103                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10104 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10105 
10106   Level: intermediate
10107 
10108   Notes:
10109   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10110   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10111   combination of other points' degrees of freedom.
10112 
10113   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10114   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10115 
10116   The reference counts of `anchorSection` and `anchorIS` are incremented.
10117 
10118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10119 @*/
10120 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10121 {
10122   DM_Plex    *plex = (DM_Plex *)dm->data;
10123   PetscMPIInt result;
10124 
10125   PetscFunctionBegin;
10126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10127   if (anchorSection) {
10128     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10129     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10130     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10131   }
10132   if (anchorIS) {
10133     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10134     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10135     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10136   }
10137 
10138   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10139   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10140   plex->anchorSection = anchorSection;
10141 
10142   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10143   PetscCall(ISDestroy(&plex->anchorIS));
10144   plex->anchorIS = anchorIS;
10145 
10146   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10147     PetscInt        size, a, pStart, pEnd;
10148     const PetscInt *anchors;
10149 
10150     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10151     PetscCall(ISGetLocalSize(anchorIS, &size));
10152     PetscCall(ISGetIndices(anchorIS, &anchors));
10153     for (a = 0; a < size; a++) {
10154       PetscInt p;
10155 
10156       p = anchors[a];
10157       if (p >= pStart && p < pEnd) {
10158         PetscInt dof;
10159 
10160         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10161         if (dof) {
10162           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10163           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10164         }
10165       }
10166     }
10167     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10168   }
10169   /* reset the generic constraints */
10170   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10171   PetscFunctionReturn(PETSC_SUCCESS);
10172 }
10173 
10174 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10175 {
10176   PetscSection anchorSection;
10177   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10178 
10179   PetscFunctionBegin;
10180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10181   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10182   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10183   PetscCall(PetscSectionGetNumFields(section, &numFields));
10184   if (numFields) {
10185     PetscInt f;
10186     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10187 
10188     for (f = 0; f < numFields; f++) {
10189       PetscInt numComp;
10190 
10191       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10192       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10193     }
10194   }
10195   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10196   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10197   pStart = PetscMax(pStart, sStart);
10198   pEnd   = PetscMin(pEnd, sEnd);
10199   pEnd   = PetscMax(pStart, pEnd);
10200   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10201   for (p = pStart; p < pEnd; p++) {
10202     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10203     if (dof) {
10204       PetscCall(PetscSectionGetDof(section, p, &dof));
10205       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10206       for (f = 0; f < numFields; f++) {
10207         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10208         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10209       }
10210     }
10211   }
10212   PetscCall(PetscSectionSetUp(*cSec));
10213   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10214   PetscFunctionReturn(PETSC_SUCCESS);
10215 }
10216 
10217 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10218 {
10219   PetscSection    aSec;
10220   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10221   const PetscInt *anchors;
10222   PetscInt        numFields, f;
10223   IS              aIS;
10224   MatType         mtype;
10225   PetscBool       iscuda, iskokkos;
10226 
10227   PetscFunctionBegin;
10228   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10229   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10230   PetscCall(PetscSectionGetStorageSize(section, &n));
10231   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10232   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10233   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10234   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10235   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10236   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10237   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10238   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10239   else mtype = MATSEQAIJ;
10240   PetscCall(MatSetType(*cMat, mtype));
10241   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10242   PetscCall(ISGetIndices(aIS, &anchors));
10243   /* cSec will be a subset of aSec and section */
10244   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10245   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10246   PetscCall(PetscMalloc1(m + 1, &i));
10247   i[0] = 0;
10248   PetscCall(PetscSectionGetNumFields(section, &numFields));
10249   for (p = pStart; p < pEnd; p++) {
10250     PetscInt rDof, rOff, r;
10251 
10252     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10253     if (!rDof) continue;
10254     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10255     if (numFields) {
10256       for (f = 0; f < numFields; f++) {
10257         annz = 0;
10258         for (r = 0; r < rDof; r++) {
10259           a = anchors[rOff + r];
10260           if (a < sStart || a >= sEnd) continue;
10261           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10262           annz += aDof;
10263         }
10264         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10265         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10266         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10267       }
10268     } else {
10269       annz = 0;
10270       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10271       for (q = 0; q < dof; q++) {
10272         a = anchors[rOff + q];
10273         if (a < sStart || a >= sEnd) continue;
10274         PetscCall(PetscSectionGetDof(section, a, &aDof));
10275         annz += aDof;
10276       }
10277       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10278       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10279       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10280     }
10281   }
10282   nnz = i[m];
10283   PetscCall(PetscMalloc1(nnz, &j));
10284   offset = 0;
10285   for (p = pStart; p < pEnd; p++) {
10286     if (numFields) {
10287       for (f = 0; f < numFields; f++) {
10288         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10289         for (q = 0; q < dof; q++) {
10290           PetscInt rDof, rOff, r;
10291           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10292           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10293           for (r = 0; r < rDof; r++) {
10294             PetscInt s;
10295 
10296             a = anchors[rOff + r];
10297             if (a < sStart || a >= sEnd) continue;
10298             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10299             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10300             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10301           }
10302         }
10303       }
10304     } else {
10305       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10306       for (q = 0; q < dof; q++) {
10307         PetscInt rDof, rOff, r;
10308         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10309         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10310         for (r = 0; r < rDof; r++) {
10311           PetscInt s;
10312 
10313           a = anchors[rOff + r];
10314           if (a < sStart || a >= sEnd) continue;
10315           PetscCall(PetscSectionGetDof(section, a, &aDof));
10316           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10317           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10318         }
10319       }
10320     }
10321   }
10322   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10323   PetscCall(PetscFree(i));
10324   PetscCall(PetscFree(j));
10325   PetscCall(ISRestoreIndices(aIS, &anchors));
10326   PetscFunctionReturn(PETSC_SUCCESS);
10327 }
10328 
10329 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10330 {
10331   DM_Plex     *plex = (DM_Plex *)dm->data;
10332   PetscSection anchorSection, section, cSec;
10333   Mat          cMat;
10334 
10335   PetscFunctionBegin;
10336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10337   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10338   if (anchorSection) {
10339     PetscInt Nf;
10340 
10341     PetscCall(DMGetLocalSection(dm, &section));
10342     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10343     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10344     PetscCall(DMGetNumFields(dm, &Nf));
10345     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10346     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10347     PetscCall(PetscSectionDestroy(&cSec));
10348     PetscCall(MatDestroy(&cMat));
10349   }
10350   PetscFunctionReturn(PETSC_SUCCESS);
10351 }
10352 
10353 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10354 {
10355   IS           subis;
10356   PetscSection section, subsection;
10357 
10358   PetscFunctionBegin;
10359   PetscCall(DMGetLocalSection(dm, &section));
10360   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10361   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10362   /* Create subdomain */
10363   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10364   /* Create submodel */
10365   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10366   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10367   PetscCall(DMSetLocalSection(*subdm, subsection));
10368   PetscCall(PetscSectionDestroy(&subsection));
10369   PetscCall(DMCopyDisc(dm, *subdm));
10370   /* Create map from submodel to global model */
10371   if (is) {
10372     PetscSection    sectionGlobal, subsectionGlobal;
10373     IS              spIS;
10374     const PetscInt *spmap;
10375     PetscInt       *subIndices;
10376     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10377     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10378 
10379     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10380     PetscCall(ISGetIndices(spIS, &spmap));
10381     PetscCall(PetscSectionGetNumFields(section, &Nf));
10382     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10383     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10384     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10385     for (p = pStart; p < pEnd; ++p) {
10386       PetscInt gdof, pSubSize = 0;
10387 
10388       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10389       if (gdof > 0) {
10390         for (f = 0; f < Nf; ++f) {
10391           PetscInt fdof, fcdof;
10392 
10393           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10394           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10395           pSubSize += fdof - fcdof;
10396         }
10397         subSize += pSubSize;
10398         if (pSubSize) {
10399           if (bs < 0) {
10400             bs = pSubSize;
10401           } else if (bs != pSubSize) {
10402             /* Layout does not admit a pointwise block size */
10403             bs = 1;
10404           }
10405         }
10406       }
10407     }
10408     /* Must have same blocksize on all procs (some might have no points) */
10409     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10410     bsLocal[1] = bs;
10411     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10412     if (bsMinMax[0] != bsMinMax[1]) {
10413       bs = 1;
10414     } else {
10415       bs = bsMinMax[0];
10416     }
10417     PetscCall(PetscMalloc1(subSize, &subIndices));
10418     for (p = pStart; p < pEnd; ++p) {
10419       PetscInt gdof, goff;
10420 
10421       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10422       if (gdof > 0) {
10423         const PetscInt point = spmap[p];
10424 
10425         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10426         for (f = 0; f < Nf; ++f) {
10427           PetscInt fdof, fcdof, fc, f2, poff = 0;
10428 
10429           /* Can get rid of this loop by storing field information in the global section */
10430           for (f2 = 0; f2 < f; ++f2) {
10431             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10432             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10433             poff += fdof - fcdof;
10434           }
10435           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10436           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10437           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10438         }
10439       }
10440     }
10441     PetscCall(ISRestoreIndices(spIS, &spmap));
10442     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10443     if (bs > 1) {
10444       /* We need to check that the block size does not come from non-contiguous fields */
10445       PetscInt i, j, set = 1;
10446       for (i = 0; i < subSize; i += bs) {
10447         for (j = 0; j < bs; ++j) {
10448           if (subIndices[i + j] != subIndices[i] + j) {
10449             set = 0;
10450             break;
10451           }
10452         }
10453       }
10454       if (set) PetscCall(ISSetBlockSize(*is, bs));
10455     }
10456     /* Attach nullspace */
10457     for (f = 0; f < Nf; ++f) {
10458       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10459       if ((*subdm)->nullspaceConstructors[f]) break;
10460     }
10461     if (f < Nf) {
10462       MatNullSpace nullSpace;
10463       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10464 
10465       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10466       PetscCall(MatNullSpaceDestroy(&nullSpace));
10467     }
10468   }
10469   PetscFunctionReturn(PETSC_SUCCESS);
10470 }
10471 
10472 /*@
10473   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10474 
10475   Input Parameters:
10476 + dm    - The `DM`
10477 - dummy - unused argument
10478 
10479   Options Database Key:
10480 . -dm_plex_monitor_throughput - Activate the monitor
10481 
10482   Level: developer
10483 
10484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10485 @*/
10486 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10487 {
10488   PetscLogHandler default_handler;
10489 
10490   PetscFunctionBegin;
10491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10492   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10493   if (default_handler) {
10494     PetscLogEvent      event;
10495     PetscEventPerfInfo eventInfo;
10496     PetscReal          cellRate, flopRate;
10497     PetscInt           cStart, cEnd, Nf, N;
10498     const char        *name;
10499 
10500     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10501     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10502     PetscCall(DMGetNumFields(dm, &Nf));
10503     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10504     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10505     N        = (cEnd - cStart) * Nf * eventInfo.count;
10506     flopRate = eventInfo.flops / eventInfo.time;
10507     cellRate = N / eventInfo.time;
10508     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)));
10509   } else {
10510     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.");
10511   }
10512   PetscFunctionReturn(PETSC_SUCCESS);
10513 }
10514