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