xref: /petsc/src/dm/impls/plex/plex.c (revision 34c645fd3b0199e05bec2fcc32d3597bfeb7f4f2)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
89 
90   PetscFunctionBegin;
91   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
93   PetscCall(ISGetLocalSize(valueIS, &Nct));
94   PetscCall(ISGetIndices(valueIS, &ctypes));
95   if (!Nct) cS = cE = 0;
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS = PetscMin(cS, ctS);
112     cE = PetscMax(cE, ctE);
113   }
114   PetscCall(ISDestroy(&valueIS));
115   // Reset label for fast lookup
116   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
117   if (cStart) *cStart = cS;
118   if (cEnd) *cEnd = cE;
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
123 {
124   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
125   PetscInt                *sStart, *sEnd;
126   PetscViewerVTKFieldType *ft;
127   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
128   DMLabel                  depthLabel, ctLabel;
129 
130   PetscFunctionBegin;
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161   }
162 
163   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
164   *types = 0;
165 
166   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
167     if (globalvcdof[c]) ++(*types);
168   }
169 
170   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
171   t = 0;
172   if (globalvcdof[DM_NUM_POLYTOPES]) {
173     sStart[t] = vStart;
174     sEnd[t]   = vEnd;
175     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
176     ++t;
177   }
178 
179   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
180     if (globalvcdof[c]) {
181       const DMPolytopeType ict = (DMPolytopeType)c;
182 
183       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
184       sStart[t] = cStart;
185       sEnd[t]   = cEnd;
186       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
187       ++t;
188     }
189   }
190 
191   if (!*types) {
192     if (field >= 0) {
193       const char *fieldname;
194 
195       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
196       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
197     } else {
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
199     }
200   }
201 
202   *ssStart = sStart;
203   *ssEnd   = sEnd;
204   *sft     = ft;
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
209 {
210   PetscFunctionBegin;
211   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
216 {
217   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
218   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
219 
220   PetscFunctionBegin;
221   *ft = PETSC_VTK_INVALID;
222   PetscCall(DMGetCoordinateDim(dm, &cdim));
223   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
224   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
225   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
226   if (field >= 0) {
227     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
228     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
229   } else {
230     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
231     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
232   }
233   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
234   if (globalvcdof[0]) {
235     *sStart = vStart;
236     *sEnd   = vEnd;
237     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
238     else *ft = PETSC_VTK_POINT_FIELD;
239   } else if (globalvcdof[1]) {
240     *sStart = cStart;
241     *sEnd   = cEnd;
242     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
243     else *ft = PETSC_VTK_CELL_FIELD;
244   } else {
245     if (field >= 0) {
246       const char *fieldname;
247 
248       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
249       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
250     } else {
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
252     }
253   }
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 /*@
258   DMPlexVecView1D - Plot many 1D solutions on the same line graph
259 
260   Collective
261 
262   Input Parameters:
263 + dm     - The `DMPLEX` object
264 . n      - The number of vectors
265 . u      - The array of local vectors
266 - viewer - The `PetscViewer`
267 
268   Level: advanced
269 
270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
271 @*/
272 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
273 {
274   PetscDS            ds;
275   PetscDraw          draw = NULL;
276   PetscDrawLG        lg;
277   Vec                coordinates;
278   const PetscScalar *coords, **sol;
279   PetscReal         *vals;
280   PetscInt          *Nc;
281   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
282   char             **names;
283 
284   PetscFunctionBegin;
285   PetscCall(DMGetDS(dm, &ds));
286   PetscCall(PetscDSGetNumFields(ds, &Nf));
287   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
288   PetscCall(PetscDSGetComponents(ds, &Nc));
289 
290   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
291   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
292   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
293 
294   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
295   for (i = 0, l = 0; i < n; ++i) {
296     const char *vname;
297 
298     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
299     for (f = 0; f < Nf; ++f) {
300       PetscObject disc;
301       const char *fname;
302       char        tmpname[PETSC_MAX_PATH_LEN];
303 
304       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
305       /* TODO Create names for components */
306       for (c = 0; c < Nc[f]; ++c, ++l) {
307         PetscCall(PetscObjectGetName(disc, &fname));
308         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
311         PetscCall(PetscStrallocpy(tmpname, &names[l]));
312       }
313     }
314   }
315   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
316   /* Just add P_1 support for now */
317   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
318   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
319   PetscCall(VecGetArrayRead(coordinates, &coords));
320   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
321   for (v = vStart; v < vEnd; ++v) {
322     PetscScalar *x, *svals;
323 
324     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
325     for (i = 0; i < n; ++i) {
326       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
327       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
328     }
329     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
330   }
331   PetscCall(VecRestoreArrayRead(coordinates, &coords));
332   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
333   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
334   PetscCall(PetscFree3(sol, names, vals));
335 
336   PetscCall(PetscDrawLGDraw(lg));
337   PetscCall(PetscDrawLGDestroy(&lg));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
342 {
343   DM dm;
344 
345   PetscFunctionBegin;
346   PetscCall(VecGetDM(u, &dm));
347   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
352 {
353   DM                 dm;
354   PetscSection       s;
355   PetscDraw          draw, popup;
356   DM                 cdm;
357   PetscSection       coordSection;
358   Vec                coordinates;
359   const PetscScalar *array;
360   PetscReal          lbound[3], ubound[3];
361   PetscReal          vbound[2], time;
362   PetscBool          flg;
363   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
364   const char        *name;
365   char               title[PETSC_MAX_PATH_LEN];
366 
367   PetscFunctionBegin;
368   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
369   PetscCall(VecGetDM(v, &dm));
370   PetscCall(DMGetCoordinateDim(dm, &dim));
371   PetscCall(DMGetLocalSection(dm, &s));
372   PetscCall(PetscSectionGetNumFields(s, &Nf));
373   PetscCall(DMGetCoarsenLevel(dm, &level));
374   PetscCall(DMGetCoordinateDM(dm, &cdm));
375   PetscCall(DMGetLocalSection(cdm, &coordSection));
376   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
377   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
378   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
379 
380   PetscCall(PetscObjectGetName((PetscObject)v, &name));
381   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
382 
383   PetscCall(VecGetLocalSize(coordinates, &N));
384   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
385   PetscCall(PetscDrawClear(draw));
386 
387   /* Could implement something like DMDASelectFields() */
388   for (f = 0; f < Nf; ++f) {
389     DM          fdm = dm;
390     Vec         fv  = v;
391     IS          fis;
392     char        prefix[PETSC_MAX_PATH_LEN];
393     const char *fname;
394 
395     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
396     PetscCall(PetscSectionGetFieldName(s, f, &fname));
397 
398     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
399     else prefix[0] = '\0';
400     if (Nf > 1) {
401       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
402       PetscCall(VecGetSubVector(v, fis, &fv));
403       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
404       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
405     }
406     for (comp = 0; comp < Nc; ++comp, ++w) {
407       PetscInt nmax = 2;
408 
409       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
410       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
411       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
412       PetscCall(PetscDrawSetTitle(draw, title));
413 
414       /* TODO Get max and min only for this component */
415       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
416       if (!flg) {
417         PetscCall(VecMin(fv, NULL, &vbound[0]));
418         PetscCall(VecMax(fv, NULL, &vbound[1]));
419         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
420       }
421 
422       PetscCall(PetscDrawGetPopup(draw, &popup));
423       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
424       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
425       PetscCall(VecGetArrayRead(fv, &array));
426       for (c = cStart; c < cEnd; ++c) {
427         PetscScalar       *coords = NULL, *a = NULL;
428         const PetscScalar *coords_arr;
429         PetscBool          isDG;
430         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
431 
432         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
433         if (a) {
434           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
435           color[1] = color[2] = color[3] = color[0];
436         } else {
437           PetscScalar *vals = NULL;
438           PetscInt     numVals, va;
439 
440           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
441           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
442           switch (numVals / Nc) {
443           case 3: /* P1 Triangle */
444           case 4: /* P1 Quadrangle */
445             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
446             break;
447           case 6: /* P2 Triangle */
448           case 8: /* P2 Quadrangle */
449             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
450             break;
451           default:
452             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
453           }
454           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
455         }
456         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
457         switch (numCoords) {
458         case 6:
459         case 12: /* Localized triangle */
460           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
461           break;
462         case 8:
463         case 16: /* Localized quadrilateral */
464           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
465           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
466           break;
467         default:
468           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
469         }
470         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
471       }
472       PetscCall(VecRestoreArrayRead(fv, &array));
473       PetscCall(PetscDrawFlush(draw));
474       PetscCall(PetscDrawPause(draw));
475       PetscCall(PetscDrawSave(draw));
476     }
477     if (Nf > 1) {
478       PetscCall(VecRestoreSubVector(v, fis, &fv));
479       PetscCall(ISDestroy(&fis));
480       PetscCall(DMDestroy(&fdm));
481     }
482   }
483   PetscFunctionReturn(PETSC_SUCCESS);
484 }
485 
486 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
487 {
488   DM        dm;
489   PetscDraw draw;
490   PetscInt  dim;
491   PetscBool isnull;
492 
493   PetscFunctionBegin;
494   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
495   PetscCall(PetscDrawIsNull(draw, &isnull));
496   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
497 
498   PetscCall(VecGetDM(v, &dm));
499   PetscCall(DMGetCoordinateDim(dm, &dim));
500   switch (dim) {
501   case 1:
502     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
503     break;
504   case 2:
505     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
506     break;
507   default:
508     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
509   }
510   PetscFunctionReturn(PETSC_SUCCESS);
511 }
512 
513 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
514 {
515   DM                      dm;
516   Vec                     locv;
517   const char             *name;
518   PetscSection            section;
519   PetscInt                pStart, pEnd;
520   PetscInt                numFields;
521   PetscViewerVTKFieldType ft;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
526   PetscCall(PetscObjectGetName((PetscObject)v, &name));
527   PetscCall(PetscObjectSetName((PetscObject)locv, name));
528   PetscCall(VecCopy(v, locv));
529   PetscCall(DMGetLocalSection(dm, &section));
530   PetscCall(PetscSectionGetNumFields(section, &numFields));
531   if (!numFields) {
532     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
533     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
534   } else {
535     PetscInt f;
536 
537     for (f = 0; f < numFields; f++) {
538       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
539       if (ft == PETSC_VTK_INVALID) continue;
540       PetscCall(PetscObjectReference((PetscObject)locv));
541       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
542     }
543     PetscCall(VecDestroy(&locv));
544   }
545   PetscFunctionReturn(PETSC_SUCCESS);
546 }
547 
548 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
549 {
550   DM        dm;
551   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(v, &dm));
555   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
561   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
562     PetscInt    i, numFields;
563     PetscObject fe;
564     PetscBool   fem  = PETSC_FALSE;
565     Vec         locv = v;
566     const char *name;
567     PetscInt    step;
568     PetscReal   time;
569 
570     PetscCall(DMGetNumFields(dm, &numFields));
571     for (i = 0; i < numFields; i++) {
572       PetscCall(DMGetField(dm, i, NULL, &fe));
573       if (fe->classid == PETSCFE_CLASSID) {
574         fem = PETSC_TRUE;
575         break;
576       }
577     }
578     if (fem) {
579       PetscObject isZero;
580 
581       PetscCall(DMGetLocalVector(dm, &locv));
582       PetscCall(PetscObjectGetName((PetscObject)v, &name));
583       PetscCall(PetscObjectSetName((PetscObject)locv, name));
584       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
585       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
586       PetscCall(VecCopy(v, locv));
587       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
588       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
589     }
590     if (isvtk) {
591       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
592     } else if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
595 #else
596       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598     } else if (isdraw) {
599       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
600     } else if (isglvis) {
601       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
602       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
603       PetscCall(VecView_GLVis(locv, viewer));
604     } else if (iscgns) {
605 #if defined(PETSC_HAVE_CGNS)
606       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
607 #else
608       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
609 #endif
610     }
611     if (fem) {
612       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
613       PetscCall(DMRestoreLocalVector(dm, &locv));
614     }
615   } else {
616     PetscBool isseq;
617 
618     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
619     if (isseq) PetscCall(VecView_Seq(v, viewer));
620     else PetscCall(VecView_MPI(v, viewer));
621   }
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
626 {
627   DM        dm;
628   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (isvtk || isdraw || isglvis || iscgns) {
640     Vec         locv;
641     PetscObject isZero;
642     const char *name;
643 
644     PetscCall(DMGetLocalVector(dm, &locv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)locv, name));
647     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
648     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
649     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
650     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
651     PetscCall(VecView_Plex_Local(locv, viewer));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
653     PetscCall(DMRestoreLocalVector(dm, &locv));
654   } else if (ishdf5) {
655 #if defined(PETSC_HAVE_HDF5)
656     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
657 #else
658     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
659 #endif
660   } else if (isexodusii) {
661 #if defined(PETSC_HAVE_EXODUSII)
662     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
665 #endif
666   } else {
667     PetscBool isseq;
668 
669     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
670     if (isseq) PetscCall(VecView_Seq(v, viewer));
671     else PetscCall(VecView_MPI(v, viewer));
672   }
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   MPI_Comm          comm;
680   PetscViewerFormat format;
681   Vec               v;
682   PetscBool         isvtk, ishdf5;
683 
684   PetscFunctionBegin;
685   PetscCall(VecGetDM(originalv, &dm));
686   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
687   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
691   if (format == PETSC_VIEWER_NATIVE) {
692     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
693     /* this need a better fix */
694     if (dm->useNatural) {
695       if (dm->sfNatural) {
696         const char *vecname;
697         PetscInt    n, nroots;
698 
699         PetscCall(VecGetLocalSize(originalv, &n));
700         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
701         if (n == nroots) {
702           PetscCall(DMPlexCreateNaturalVector(dm, &v));
703           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
704           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
705           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
706           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
707         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
708       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
709     } else v = originalv;
710   } else v = originalv;
711 
712   if (ishdf5) {
713 #if defined(PETSC_HAVE_HDF5)
714     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
715 #else
716     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
717 #endif
718   } else if (isvtk) {
719     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
720   } else {
721     PetscBool isseq;
722 
723     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
724     if (isseq) PetscCall(VecView_Seq(v, viewer));
725     else PetscCall(VecView_MPI(v, viewer));
726   }
727   if (v != originalv) PetscCall(VecDestroy(&v));
728   PetscFunctionReturn(PETSC_SUCCESS);
729 }
730 
731 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
732 {
733   DM        dm;
734   PetscBool ishdf5;
735 
736   PetscFunctionBegin;
737   PetscCall(VecGetDM(v, &dm));
738   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
739   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
740   if (ishdf5) {
741     DM          dmBC;
742     Vec         gv;
743     const char *name;
744 
745     PetscCall(DMGetOutputDM(dm, &dmBC));
746     PetscCall(DMGetGlobalVector(dmBC, &gv));
747     PetscCall(PetscObjectGetName((PetscObject)v, &name));
748     PetscCall(PetscObjectSetName((PetscObject)gv, name));
749     PetscCall(VecLoad_Default(gv, viewer));
750     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
753   } else PetscCall(VecLoad_Default(v, viewer));
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
758 {
759   DM        dm;
760   PetscBool ishdf5, isexodusii;
761 
762   PetscFunctionBegin;
763   PetscCall(VecGetDM(v, &dm));
764   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
767   if (ishdf5) {
768 #if defined(PETSC_HAVE_HDF5)
769     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
770 #else
771     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
772 #endif
773   } else if (isexodusii) {
774 #if defined(PETSC_HAVE_EXODUSII)
775     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
776 #else
777     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
778 #endif
779   } else PetscCall(VecLoad_Default(v, viewer));
780   PetscFunctionReturn(PETSC_SUCCESS);
781 }
782 
783 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
784 {
785   DM                dm;
786   PetscViewerFormat format;
787   PetscBool         ishdf5;
788 
789   PetscFunctionBegin;
790   PetscCall(VecGetDM(originalv, &dm));
791   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
792   PetscCall(PetscViewerGetFormat(viewer, &format));
793   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
794   if (format == PETSC_VIEWER_NATIVE) {
795     if (dm->useNatural) {
796       if (dm->sfNatural) {
797         if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799           Vec         v;
800           const char *vecname;
801 
802           PetscCall(DMPlexCreateNaturalVector(dm, &v));
803           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
804           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
805           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
806           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
807           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
808           PetscCall(VecDestroy(&v));
809 #else
810           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
811 #endif
812         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
813       }
814     } else PetscCall(VecLoad_Default(originalv, viewer));
815   }
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
820 {
821   PetscSection       coordSection;
822   Vec                coordinates;
823   DMLabel            depthLabel, celltypeLabel;
824   const char        *name[4];
825   const PetscScalar *a;
826   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
827 
828   PetscFunctionBegin;
829   PetscCall(DMGetDimension(dm, &dim));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCoordinateSection(dm, &coordSection));
832   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
833   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
834   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
835   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
836   PetscCall(VecGetArrayRead(coordinates, &a));
837   name[0]       = "vertex";
838   name[1]       = "edge";
839   name[dim - 1] = "face";
840   name[dim]     = "cell";
841   for (c = cStart; c < cEnd; ++c) {
842     PetscInt *closure = NULL;
843     PetscInt  closureSize, cl, ct;
844 
845     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
847     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
848     PetscCall(PetscViewerASCIIPushTab(viewer));
849     for (cl = 0; cl < closureSize * 2; cl += 2) {
850       PetscInt point = closure[cl], depth, dof, off, d, p;
851 
852       if ((point < pStart) || (point >= pEnd)) continue;
853       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
854       if (!dof) continue;
855       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
856       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
858       for (p = 0; p < dof / dim; ++p) {
859         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
860         for (d = 0; d < dim; ++d) {
861           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
862           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
863         }
864         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
865       }
866       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
867     }
868     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
869     PetscCall(PetscViewerASCIIPopTab(viewer));
870   }
871   PetscCall(VecRestoreArrayRead(coordinates, &a));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 typedef enum {
876   CS_CARTESIAN,
877   CS_POLAR,
878   CS_CYLINDRICAL,
879   CS_SPHERICAL
880 } CoordSystem;
881 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
882 
883 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
884 {
885   PetscInt i;
886 
887   PetscFunctionBegin;
888   if (dim > 3) {
889     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
890   } else {
891     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
892 
893     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
894     switch (cs) {
895     case CS_CARTESIAN:
896       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
897       break;
898     case CS_POLAR:
899       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
900       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
901       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
902       break;
903     case CS_CYLINDRICAL:
904       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
905       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
906       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
907       trcoords[2] = coords[2];
908       break;
909     case CS_SPHERICAL:
910       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
911       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
912       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
913       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
914       break;
915     }
916     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
917   }
918   PetscFunctionReturn(PETSC_SUCCESS);
919 }
920 
921 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
922 {
923   DM_Plex          *mesh = (DM_Plex *)dm->data;
924   DM                cdm, cdmCell;
925   PetscSection      coordSection, coordSectionCell;
926   Vec               coordinates, coordinatesCell;
927   PetscViewerFormat format;
928 
929   PetscFunctionBegin;
930   PetscCall(PetscViewerGetFormat(viewer, &format));
931   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
932     const char *name;
933     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
934     PetscInt    pStart, pEnd, p, numLabels, l;
935     PetscMPIInt rank, size;
936 
937     PetscCall(DMGetCoordinateDM(dm, &cdm));
938     PetscCall(DMGetCoordinateSection(dm, &coordSection));
939     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
940     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
941     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
942     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
943     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
944     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
945     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
946     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
947     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
948     PetscCall(DMGetDimension(dm, &dim));
949     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
950     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
951     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
952     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
953     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
954     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
955     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
956     for (p = pStart; p < pEnd; ++p) {
957       PetscInt dof, off, s;
958 
959       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
960       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
961       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
962     }
963     PetscCall(PetscViewerFlush(viewer));
964     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
965     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
966     for (p = pStart; p < pEnd; ++p) {
967       PetscInt dof, off, c;
968 
969       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
970       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
971       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
972     }
973     PetscCall(PetscViewerFlush(viewer));
974     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
975     if (coordSection && coordinates) {
976       CoordSystem        cs = CS_CARTESIAN;
977       const PetscScalar *array, *arrayCell = NULL;
978       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
979       PetscMPIInt        rank;
980       const char        *name;
981 
982       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
983       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
984       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
985       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
986       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
987       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
988       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
989       pStart = PetscMin(pvStart, pcStart);
990       pEnd   = PetscMax(pvEnd, pcEnd);
991       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
994       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
995 
996       PetscCall(VecGetArrayRead(coordinates, &array));
997       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
998       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
999       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1000       for (p = pStart; p < pEnd; ++p) {
1001         PetscInt dof, off;
1002 
1003         if (p >= pvStart && p < pvEnd) {
1004           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1005           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1006           if (dof) {
1007             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1008             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1010           }
1011         }
1012         if (cdmCell && p >= pcStart && p < pcEnd) {
1013           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1014           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1015           if (dof) {
1016             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1017             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1019           }
1020         }
1021       }
1022       PetscCall(PetscViewerFlush(viewer));
1023       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1024       PetscCall(VecRestoreArrayRead(coordinates, &array));
1025       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1026     }
1027     PetscCall(DMGetNumLabels(dm, &numLabels));
1028     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1029     for (l = 0; l < numLabels; ++l) {
1030       DMLabel     label;
1031       PetscBool   isdepth;
1032       const char *name;
1033 
1034       PetscCall(DMGetLabelName(dm, l, &name));
1035       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1036       if (isdepth) continue;
1037       PetscCall(DMGetLabel(dm, name, &label));
1038       PetscCall(DMLabelView(label, viewer));
1039     }
1040     if (size > 1) {
1041       PetscSF sf;
1042 
1043       PetscCall(DMGetPointSF(dm, &sf));
1044       PetscCall(PetscSFView(sf, viewer));
1045     }
1046     if (mesh->periodic.face_sfs)
1047       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     if (lflg) {
1115       DMLabel lbl;
1116 
1117       PetscCall(DMGetLabel(dm, lname, &lbl));
1118       if (lbl) {
1119         PetscInt val, defval;
1120 
1121         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1122         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1123         for (c = pStart; c < pEnd; c++) {
1124           PetscInt *closure = NULL;
1125           PetscInt  closureSize;
1126 
1127           PetscCall(DMLabelGetValue(lbl, c, &val));
1128           if (val == defval) continue;
1129 
1130           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1132           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1133         }
1134       }
1135     }
1136 
1137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1138     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1139     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1140     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1141 \\documentclass[tikz]{standalone}\n\n\
1142 \\usepackage{pgflibraryshapes}\n\
1143 \\usetikzlibrary{backgrounds}\n\
1144 \\usetikzlibrary{arrows}\n\
1145 \\begin{document}\n"));
1146     if (size > 1) {
1147       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1148       for (p = 0; p < size; ++p) {
1149         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1150         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1151       }
1152       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1153     }
1154     if (drawHasse) {
1155       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1156 
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1169     }
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1171 
1172     /* Plot vertices */
1173     PetscCall(VecGetArray(coordinates, &coords));
1174     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1175     for (v = vStart; v < vEnd; ++v) {
1176       PetscInt  off, dof, d;
1177       PetscBool isLabeled = PETSC_FALSE;
1178 
1179       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1180       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1181       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1182       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1183       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1184       for (d = 0; d < dof; ++d) {
1185         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1186         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1187       }
1188       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1189       if (dim == 3) {
1190         PetscReal tmp = tcoords[1];
1191         tcoords[1]    = tcoords[2];
1192         tcoords[2]    = -tmp;
1193       }
1194       for (d = 0; d < dof; ++d) {
1195         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1196         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1197       }
1198       if (drawHasse) color = colors[0 % numColors];
1199       else color = colors[rank % numColors];
1200       for (l = 0; l < numLabels; ++l) {
1201         PetscInt val;
1202         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1203         if (val >= 0) {
1204           color     = lcolors[l % numLColors];
1205           isLabeled = PETSC_TRUE;
1206           break;
1207         }
1208       }
1209       if (drawNumbers[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1211       } else if (drawColors[0]) {
1212         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1213       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1214     }
1215     PetscCall(VecRestoreArray(coordinates, &coords));
1216     PetscCall(PetscViewerFlush(viewer));
1217     /* Plot edges */
1218     if (plotEdges) {
1219       PetscCall(VecGetArray(coordinates, &coords));
1220       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1221       for (e = eStart; e < eEnd; ++e) {
1222         const PetscInt *cone;
1223         PetscInt        coneSize, offA, offB, dof, d;
1224 
1225         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1226         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1227         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1228         PetscCall(DMPlexGetCone(dm, e, &cone));
1229         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1231         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1232         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1233         for (d = 0; d < dof; ++d) {
1234           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1235           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1236         }
1237         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1238         if (dim == 3) {
1239           PetscReal tmp = tcoords[1];
1240           tcoords[1]    = tcoords[2];
1241           tcoords[2]    = -tmp;
1242         }
1243         for (d = 0; d < dof; ++d) {
1244           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1245           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1246         }
1247         if (drawHasse) color = colors[1 % numColors];
1248         else color = colors[rank % numColors];
1249         for (l = 0; l < numLabels; ++l) {
1250           PetscInt val;
1251           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1252           if (val >= 0) {
1253             color = lcolors[l % numLColors];
1254             break;
1255           }
1256         }
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1258       }
1259       PetscCall(VecRestoreArray(coordinates, &coords));
1260       PetscCall(PetscViewerFlush(viewer));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1262     }
1263     /* Plot cells */
1264     if (dim == 3 || !drawNumbers[1]) {
1265       for (e = eStart; e < eEnd; ++e) {
1266         const PetscInt *cone;
1267 
1268         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1269         color = colors[rank % numColors];
1270         for (l = 0; l < numLabels; ++l) {
1271           PetscInt val;
1272           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1273           if (val >= 0) {
1274             color = lcolors[l % numLColors];
1275             break;
1276           }
1277         }
1278         PetscCall(DMPlexGetCone(dm, e, &cone));
1279         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1280       }
1281     } else {
1282       DMPolytopeType ct;
1283 
1284       /* Drawing a 2D polygon */
1285       for (c = cStart; c < cEnd; ++c) {
1286         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1287         PetscCall(DMPlexGetCellType(dm, c, &ct));
1288         if (DMPolytopeTypeIsHybrid(ct)) {
1289           const PetscInt *cone;
1290           PetscInt        coneSize, e;
1291 
1292           PetscCall(DMPlexGetCone(dm, c, &cone));
1293           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1294           for (e = 0; e < coneSize; ++e) {
1295             const PetscInt *econe;
1296 
1297             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1298             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1299           }
1300         } else {
1301           PetscInt *closure = NULL;
1302           PetscInt  closureSize, Nv = 0, v;
1303 
1304           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1305           for (p = 0; p < closureSize * 2; p += 2) {
1306             const PetscInt point = closure[p];
1307 
1308             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1309           }
1310           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1311           for (v = 0; v <= Nv; ++v) {
1312             const PetscInt vertex = closure[v % Nv];
1313 
1314             if (v > 0) {
1315               if (plotEdges) {
1316                 const PetscInt *edge;
1317                 PetscInt        endpoints[2], ne;
1318 
1319                 endpoints[0] = closure[v - 1];
1320                 endpoints[1] = vertex;
1321                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1322                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1323                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1324                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1325               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1326             }
1327             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1328           }
1329           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1330           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331         }
1332       }
1333     }
1334     for (c = cStart; c < cEnd; ++c) {
1335       double             ccoords[3] = {0.0, 0.0, 0.0};
1336       PetscBool          isLabeled  = PETSC_FALSE;
1337       PetscScalar       *cellCoords = NULL;
1338       const PetscScalar *array;
1339       PetscInt           numCoords, cdim, d;
1340       PetscBool          isDG;
1341 
1342       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1343       PetscCall(DMGetCoordinateDim(dm, &cdim));
1344       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1345       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1346       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1347       for (p = 0; p < numCoords / cdim; ++p) {
1348         for (d = 0; d < cdim; ++d) {
1349           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1350           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1351         }
1352         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1353         if (cdim == 3) {
1354           PetscReal tmp = tcoords[1];
1355           tcoords[1]    = tcoords[2];
1356           tcoords[2]    = -tmp;
1357         }
1358         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1359       }
1360       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1361       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1362       for (d = 0; d < cdim; ++d) {
1363         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1364         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1365       }
1366       if (drawHasse) color = colors[depth % numColors];
1367       else color = colors[rank % numColors];
1368       for (l = 0; l < numLabels; ++l) {
1369         PetscInt val;
1370         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1371         if (val >= 0) {
1372           color     = lcolors[l % numLColors];
1373           isLabeled = PETSC_TRUE;
1374           break;
1375         }
1376       }
1377       if (drawNumbers[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1379       } else if (drawColors[dim]) {
1380         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1381       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1382     }
1383     if (drawHasse) {
1384       color = colors[depth % numColors];
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1389       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1390 
1391       color = colors[1 % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       color = colors[0 % numColors];
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1403       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1404 
1405       for (p = pStart; p < pEnd; ++p) {
1406         const PetscInt *cone;
1407         PetscInt        coneSize, cp;
1408 
1409         PetscCall(DMPlexGetCone(dm, p, &cone));
1410         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1411         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1412       }
1413     }
1414     PetscCall(PetscViewerFlush(viewer));
1415     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1417     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1418     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1419     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1420     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1421     PetscCall(PetscFree3(names, colors, lcolors));
1422     PetscCall(PetscBTDestroy(&wp));
1423   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1424     Vec                    cown, acown;
1425     VecScatter             sct;
1426     ISLocalToGlobalMapping g2l;
1427     IS                     gid, acis;
1428     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1429     MPI_Group              ggroup, ngroup;
1430     PetscScalar           *array, nid;
1431     const PetscInt        *idxs;
1432     PetscInt              *idxs2, *start, *adjacency, *work;
1433     PetscInt64             lm[3], gm[3];
1434     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1435     PetscMPIInt            d1, d2, rank;
1436 
1437     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1438     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1439 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1440     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1441 #endif
1442     if (ncomm != MPI_COMM_NULL) {
1443       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1444       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1445       d1 = 0;
1446       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1447       nid = d2;
1448       PetscCallMPI(MPI_Group_free(&ggroup));
1449       PetscCallMPI(MPI_Group_free(&ngroup));
1450       PetscCallMPI(MPI_Comm_free(&ncomm));
1451     } else nid = 0.0;
1452 
1453     /* Get connectivity */
1454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1455     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1456 
1457     /* filter overlapped local cells */
1458     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1459     PetscCall(ISGetIndices(gid, &idxs));
1460     PetscCall(ISGetLocalSize(gid, &cum));
1461     PetscCall(PetscMalloc1(cum, &idxs2));
1462     for (c = cStart, cum = 0; c < cEnd; c++) {
1463       if (idxs[c - cStart] < 0) continue;
1464       idxs2[cum++] = idxs[c - cStart];
1465     }
1466     PetscCall(ISRestoreIndices(gid, &idxs));
1467     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1468     PetscCall(ISDestroy(&gid));
1469     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1470 
1471     /* support for node-aware cell locality */
1472     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1473     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1474     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1475     PetscCall(VecGetArray(cown, &array));
1476     for (c = 0; c < numVertices; c++) array[c] = nid;
1477     PetscCall(VecRestoreArray(cown, &array));
1478     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1479     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1481     PetscCall(ISDestroy(&acis));
1482     PetscCall(VecScatterDestroy(&sct));
1483     PetscCall(VecDestroy(&cown));
1484 
1485     /* compute edgeCut */
1486     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1487     PetscCall(PetscMalloc1(cum, &work));
1488     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1489     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1490     PetscCall(ISDestroy(&gid));
1491     PetscCall(VecGetArray(acown, &array));
1492     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1493       PetscInt totl;
1494 
1495       totl = start[c + 1] - start[c];
1496       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1497       for (i = 0; i < totl; i++) {
1498         if (work[i] < 0) {
1499           ect += 1;
1500           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1501         }
1502       }
1503     }
1504     PetscCall(PetscFree(work));
1505     PetscCall(VecRestoreArray(acown, &array));
1506     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1507     lm[1] = -numVertices;
1508     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1509     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1510     lm[0] = ect;                     /* edgeCut */
1511     lm[1] = ectn;                    /* node-aware edgeCut */
1512     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1513     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1514     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1515 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1517 #else
1518     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1519 #endif
1520     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1521     PetscCall(PetscFree(start));
1522     PetscCall(PetscFree(adjacency));
1523     PetscCall(VecDestroy(&acown));
1524   } else {
1525     const char    *name;
1526     PetscInt      *sizes, *hybsizes, *ghostsizes;
1527     PetscInt       locDepth, depth, cellHeight, dim, d;
1528     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1529     PetscInt       numLabels, l, maxSize = 17;
1530     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1531     MPI_Comm       comm;
1532     PetscMPIInt    size, rank;
1533 
1534     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1535     PetscCallMPI(MPI_Comm_size(comm, &size));
1536     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1537     PetscCall(DMGetDimension(dm, &dim));
1538     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1539     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1540     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1541     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1542     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1543     PetscCall(DMPlexGetDepth(dm, &locDepth));
1544     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1545     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1546     gcNum = gcEnd - gcStart;
1547     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1548     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1549     for (d = 0; d <= depth; d++) {
1550       PetscInt Nc[2] = {0, 0}, ict;
1551 
1552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1553       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1554       ict = ct0;
1555       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1556       ct0 = (DMPolytopeType)ict;
1557       for (p = pStart; p < pEnd; ++p) {
1558         DMPolytopeType ct;
1559 
1560         PetscCall(DMPlexGetCellType(dm, p, &ct));
1561         if (ct == ct0) ++Nc[0];
1562         else ++Nc[1];
1563       }
1564       if (size < maxSize) {
1565         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1566         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1567         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1568         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1569         for (p = 0; p < size; ++p) {
1570           if (rank == 0) {
1571             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1572             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1573             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1574           }
1575         }
1576       } else {
1577         PetscInt locMinMax[2];
1578 
1579         locMinMax[0] = Nc[0] + Nc[1];
1580         locMinMax[1] = Nc[0] + Nc[1];
1581         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1582         locMinMax[0] = Nc[1];
1583         locMinMax[1] = Nc[1];
1584         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1585         if (d == depth) {
1586           locMinMax[0] = gcNum;
1587           locMinMax[1] = gcNum;
1588           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1589         }
1590         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1591         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1592         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1593         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1594       }
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1596     }
1597     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1598     {
1599       const PetscReal *maxCell;
1600       const PetscReal *L;
1601       PetscBool        localized;
1602 
1603       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1604       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1605       if (L || localized) {
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1607         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1608         if (L) {
1609           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1610           for (d = 0; d < dim; ++d) {
1611             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1612             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1613           }
1614           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1615         }
1616         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1617         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1618       }
1619     }
1620     PetscCall(DMGetNumLabels(dm, &numLabels));
1621     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1622     for (l = 0; l < numLabels; ++l) {
1623       DMLabel         label;
1624       const char     *name;
1625       IS              valueIS;
1626       const PetscInt *values;
1627       PetscInt        numValues, v;
1628 
1629       PetscCall(DMGetLabelName(dm, l, &name));
1630       PetscCall(DMGetLabel(dm, name, &label));
1631       PetscCall(DMLabelGetNumValues(label, &numValues));
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1633       PetscCall(DMLabelGetValueIS(label, &valueIS));
1634       PetscCall(ISGetIndices(valueIS, &values));
1635       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1636       for (v = 0; v < numValues; ++v) {
1637         PetscInt size;
1638 
1639         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1640         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1642       }
1643       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1644       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1645       PetscCall(ISRestoreIndices(valueIS, &values));
1646       PetscCall(ISDestroy(&valueIS));
1647     }
1648     {
1649       char    **labelNames;
1650       PetscInt  Nl = numLabels;
1651       PetscBool flg;
1652 
1653       PetscCall(PetscMalloc1(Nl, &labelNames));
1654       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1655       for (l = 0; l < Nl; ++l) {
1656         DMLabel label;
1657 
1658         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1659         if (flg) {
1660           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1661           PetscCall(DMLabelView(label, viewer));
1662         }
1663         PetscCall(PetscFree(labelNames[l]));
1664       }
1665       PetscCall(PetscFree(labelNames));
1666     }
1667     /* If no fields are specified, people do not want to see adjacency */
1668     if (dm->Nf) {
1669       PetscInt f;
1670 
1671       for (f = 0; f < dm->Nf; ++f) {
1672         const char *name;
1673 
1674         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1675         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1676         PetscCall(PetscViewerASCIIPushTab(viewer));
1677         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1678         if (dm->fields[f].adjacency[0]) {
1679           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1680           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1681         } else {
1682           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1683           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1684         }
1685         PetscCall(PetscViewerASCIIPopTab(viewer));
1686       }
1687     }
1688     PetscCall(DMGetCoarseDM(dm, &cdm));
1689     if (cdm) {
1690       PetscCall(PetscViewerASCIIPushTab(viewer));
1691       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1692       PetscCall(DMPlexView_Ascii(cdm, viewer));
1693       PetscCall(PetscViewerASCIIPopTab(viewer));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1700 {
1701   DMPolytopeType ct;
1702   PetscMPIInt    rank;
1703   PetscInt       cdim;
1704 
1705   PetscFunctionBegin;
1706   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1707   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1708   PetscCall(DMGetCoordinateDim(dm, &cdim));
1709   switch (ct) {
1710   case DM_POLYTOPE_SEGMENT:
1711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1712     switch (cdim) {
1713     case 1: {
1714       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1715       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1716 
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1719       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1720     } break;
1721     case 2: {
1722       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1723       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1724       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1725 
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1727       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1728       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1729     } break;
1730     default:
1731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1732     }
1733     break;
1734   case DM_POLYTOPE_TRIANGLE:
1735     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1738     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1739     break;
1740   case DM_POLYTOPE_QUADRILATERAL:
1741     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1742     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1746     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1747     break;
1748   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1749     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1750     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_FV_GHOST:
1757     break;
1758   default:
1759     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1760   }
1761   PetscFunctionReturn(PETSC_SUCCESS);
1762 }
1763 
1764 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1765 {
1766   PetscReal   centroid[2] = {0., 0.};
1767   PetscMPIInt rank;
1768   PetscInt    fillColor;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1773   for (PetscInt v = 0; v < Nv; ++v) {
1774     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1775     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1776   }
1777   for (PetscInt e = 0; e < Nv; ++e) {
1778     refCoords[0] = refVertices[e * 2 + 0];
1779     refCoords[1] = refVertices[e * 2 + 1];
1780     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1781       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1782       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1783     }
1784     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1785     for (PetscInt d = 0; d < edgeDiv; ++d) {
1786       PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1787       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1788     }
1789   }
1790   PetscFunctionReturn(PETSC_SUCCESS);
1791 }
1792 
1793 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1794 {
1795   DMPolytopeType ct;
1796 
1797   PetscFunctionBegin;
1798   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1799   switch (ct) {
1800   case DM_POLYTOPE_TRIANGLE: {
1801     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1802 
1803     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1804   } break;
1805   case DM_POLYTOPE_QUADRILATERAL: {
1806     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1807 
1808     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1809   } break;
1810   default:
1811     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1812   }
1813   PetscFunctionReturn(PETSC_SUCCESS);
1814 }
1815 
1816 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1817 {
1818   PetscDraw    draw;
1819   DM           cdm;
1820   PetscSection coordSection;
1821   Vec          coordinates;
1822   PetscReal    xyl[3], xyr[3];
1823   PetscReal   *refCoords, *edgeCoords;
1824   PetscBool    isnull, drawAffine;
1825   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1826 
1827   PetscFunctionBegin;
1828   PetscCall(DMGetCoordinateDim(dm, &dim));
1829   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1830   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1831   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1832   edgeDiv    = cDegree + 1;
1833   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1834   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1835   PetscCall(DMGetCoordinateDM(dm, &cdm));
1836   PetscCall(DMGetLocalSection(cdm, &coordSection));
1837   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1838   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1839   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1840 
1841   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1842   PetscCall(PetscDrawIsNull(draw, &isnull));
1843   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1844   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1845 
1846   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1847   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1848   PetscCall(PetscDrawClear(draw));
1849 
1850   for (c = cStart; c < cEnd; ++c) {
1851     PetscScalar       *coords = NULL;
1852     const PetscScalar *coords_arr;
1853     PetscInt           numCoords;
1854     PetscBool          isDG;
1855 
1856     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1857     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1858     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1859     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1860   }
1861   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1862   PetscCall(PetscDrawFlush(draw));
1863   PetscCall(PetscDrawPause(draw));
1864   PetscCall(PetscDrawSave(draw));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1869 {
1870   DM           odm = dm, rdm = dm, cdm;
1871   PetscFE      fe;
1872   PetscSpace   sp;
1873   PetscClassId id;
1874   PetscInt     degree;
1875   PetscBool    hoView = PETSC_TRUE;
1876 
1877   PetscFunctionBegin;
1878   PetscObjectOptionsBegin((PetscObject)dm);
1879   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1880   PetscOptionsEnd();
1881   PetscCall(PetscObjectReference((PetscObject)dm));
1882   *hdm = dm;
1883   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1884   PetscCall(DMGetCoordinateDM(dm, &cdm));
1885   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1886   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1887   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1888   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1889   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1890   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1891     DM  cdm, rcdm;
1892     Mat In;
1893     Vec cl, rcl;
1894 
1895     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1896     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1897     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1898     PetscCall(DMGetCoordinateDM(odm, &cdm));
1899     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1900     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1901     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1902     PetscCall(DMSetCoarseDM(rcdm, cdm));
1903     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1904     PetscCall(MatMult(In, cl, rcl));
1905     PetscCall(MatDestroy(&In));
1906     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1907     PetscCall(DMDestroy(&odm));
1908     odm = rdm;
1909   }
1910   *hdm = rdm;
1911   PetscFunctionReturn(PETSC_SUCCESS);
1912 }
1913 
1914 #if defined(PETSC_HAVE_EXODUSII)
1915   #include <exodusII.h>
1916   #include <petscviewerexodusii.h>
1917 #endif
1918 
1919 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1920 {
1921   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1922   char      name[PETSC_MAX_PATH_LEN];
1923 
1924   PetscFunctionBegin;
1925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1926   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1932   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1933   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1934   if (iascii) {
1935     PetscViewerFormat format;
1936     PetscCall(PetscViewerGetFormat(viewer, &format));
1937     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1938     else PetscCall(DMPlexView_Ascii(dm, viewer));
1939   } else if (ishdf5) {
1940 #if defined(PETSC_HAVE_HDF5)
1941     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1942 #else
1943     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1944 #endif
1945   } else if (isvtk) {
1946     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1947   } else if (isdraw) {
1948     DM hdm;
1949 
1950     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1951     PetscCall(DMPlexView_Draw(hdm, viewer));
1952     PetscCall(DMDestroy(&hdm));
1953   } else if (isglvis) {
1954     PetscCall(DMPlexView_GLVis(dm, viewer));
1955 #if defined(PETSC_HAVE_EXODUSII)
1956   } else if (isexodus) {
1957     /*
1958       exodusII requires that all sets be part of exactly one cell set.
1959       If the dm does not have a "Cell Sets" label defined, we create one
1960       with ID 1, containing all cells.
1961       Note that if the Cell Sets label is defined but does not cover all cells,
1962       we may still have a problem. This should probably be checked here or in the viewer;
1963     */
1964     PetscInt numCS;
1965     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1966     if (!numCS) {
1967       PetscInt cStart, cEnd, c;
1968       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1969       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1970       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1971     }
1972     PetscCall(DMView_PlexExodusII(dm, viewer));
1973 #endif
1974 #if defined(PETSC_HAVE_CGNS)
1975   } else if (iscgns) {
1976     PetscCall(DMView_PlexCGNS(dm, viewer));
1977 #endif
1978   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1979   /* Optionally view the partition */
1980   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1981   if (flg) {
1982     Vec ranks;
1983     PetscCall(DMPlexCreateRankField(dm, &ranks));
1984     PetscCall(VecView(ranks, viewer));
1985     PetscCall(VecDestroy(&ranks));
1986   }
1987   /* Optionally view a label */
1988   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1989   if (flg) {
1990     DMLabel label;
1991     Vec     val;
1992 
1993     PetscCall(DMGetLabel(dm, name, &label));
1994     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1995     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1996     PetscCall(VecView(val, viewer));
1997     PetscCall(VecDestroy(&val));
1998   }
1999   PetscFunctionReturn(PETSC_SUCCESS);
2000 }
2001 
2002 /*@
2003   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2004 
2005   Collective
2006 
2007   Input Parameters:
2008 + dm     - The `DM` whose topology is to be saved
2009 - viewer - The `PetscViewer` to save it in
2010 
2011   Level: advanced
2012 
2013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2014 @*/
2015 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2016 {
2017   PetscBool ishdf5;
2018 
2019   PetscFunctionBegin;
2020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2021   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2022   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2023   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2024   if (ishdf5) {
2025 #if defined(PETSC_HAVE_HDF5)
2026     PetscViewerFormat format;
2027     PetscCall(PetscViewerGetFormat(viewer, &format));
2028     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2029       IS globalPointNumbering;
2030 
2031       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2032       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2033       PetscCall(ISDestroy(&globalPointNumbering));
2034     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2035 #else
2036     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2037 #endif
2038   }
2039   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2040   PetscFunctionReturn(PETSC_SUCCESS);
2041 }
2042 
2043 /*@
2044   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2045 
2046   Collective
2047 
2048   Input Parameters:
2049 + dm     - The `DM` whose coordinates are to be saved
2050 - viewer - The `PetscViewer` for saving
2051 
2052   Level: advanced
2053 
2054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2055 @*/
2056 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2057 {
2058   PetscBool ishdf5;
2059 
2060   PetscFunctionBegin;
2061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2062   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2064   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2065   if (ishdf5) {
2066 #if defined(PETSC_HAVE_HDF5)
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2070       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2071     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   }
2076   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2077   PetscFunctionReturn(PETSC_SUCCESS);
2078 }
2079 
2080 /*@
2081   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2082 
2083   Collective
2084 
2085   Input Parameters:
2086 + dm     - The `DM` whose labels are to be saved
2087 - viewer - The `PetscViewer` for saving
2088 
2089   Level: advanced
2090 
2091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2092 @*/
2093 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2094 {
2095   PetscBool ishdf5;
2096 
2097   PetscFunctionBegin;
2098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2099   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2100   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2101   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2102   if (ishdf5) {
2103 #if defined(PETSC_HAVE_HDF5)
2104     IS                globalPointNumbering;
2105     PetscViewerFormat format;
2106 
2107     PetscCall(PetscViewerGetFormat(viewer, &format));
2108     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2109       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2110       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2111       PetscCall(ISDestroy(&globalPointNumbering));
2112     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2113 #else
2114     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2115 #endif
2116   }
2117   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2118   PetscFunctionReturn(PETSC_SUCCESS);
2119 }
2120 
2121 /*@
2122   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2123 
2124   Collective
2125 
2126   Input Parameters:
2127 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2128 . viewer    - The `PetscViewer` for saving
2129 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2130 
2131   Level: advanced
2132 
2133   Notes:
2134   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (`dm`) points. When the topology (`dm`) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
2135 
2136   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2137 
2138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2139 @*/
2140 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2141 {
2142   PetscBool ishdf5;
2143 
2144   PetscFunctionBegin;
2145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2146   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2147   if (!sectiondm) sectiondm = dm;
2148   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2149   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2150   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2151   if (ishdf5) {
2152 #if defined(PETSC_HAVE_HDF5)
2153     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2154 #else
2155     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2156 #endif
2157   }
2158   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2159   PetscFunctionReturn(PETSC_SUCCESS);
2160 }
2161 
2162 /*@
2163   DMPlexGlobalVectorView - Saves a global vector
2164 
2165   Collective
2166 
2167   Input Parameters:
2168 + dm        - The `DM` that represents the topology
2169 . viewer    - The `PetscViewer` to save data with
2170 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2171 - vec       - The global vector to be saved
2172 
2173   Level: advanced
2174 
2175   Notes:
2176   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2177 
2178   Calling sequence:
2179 .vb
2180        DMCreate(PETSC_COMM_WORLD, &dm);
2181        DMSetType(dm, DMPLEX);
2182        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2183        DMClone(dm, &sectiondm);
2184        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2185        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2186        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2187        PetscSectionSetChart(section, pStart, pEnd);
2188        PetscSectionSetUp(section);
2189        DMSetLocalSection(sectiondm, section);
2190        PetscSectionDestroy(&section);
2191        DMGetGlobalVector(sectiondm, &vec);
2192        PetscObjectSetName((PetscObject)vec, "vec_name");
2193        DMPlexTopologyView(dm, viewer);
2194        DMPlexSectionView(dm, viewer, sectiondm);
2195        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2196        DMRestoreGlobalVector(sectiondm, &vec);
2197        DMDestroy(&sectiondm);
2198        DMDestroy(&dm);
2199 .ve
2200 
2201 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2202 @*/
2203 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2204 {
2205   PetscBool ishdf5;
2206 
2207   PetscFunctionBegin;
2208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2209   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2210   if (!sectiondm) sectiondm = dm;
2211   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2212   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2213   /* Check consistency */
2214   {
2215     PetscSection section;
2216     PetscBool    includesConstraints;
2217     PetscInt     m, m1;
2218 
2219     PetscCall(VecGetLocalSize(vec, &m1));
2220     PetscCall(DMGetGlobalSection(sectiondm, &section));
2221     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2222     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2223     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2224     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2225   }
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2231 #else
2232     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2233 #endif
2234   }
2235   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2236   PetscFunctionReturn(PETSC_SUCCESS);
2237 }
2238 
2239 /*@
2240   DMPlexLocalVectorView - Saves a local vector
2241 
2242   Collective
2243 
2244   Input Parameters:
2245 + dm        - The `DM` that represents the topology
2246 . viewer    - The `PetscViewer` to save data with
2247 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2248 - vec       - The local vector to be saved
2249 
2250   Level: advanced
2251 
2252   Note:
2253   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2254 
2255   Calling sequence:
2256 .vb
2257        DMCreate(PETSC_COMM_WORLD, &dm);
2258        DMSetType(dm, DMPLEX);
2259        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2260        DMClone(dm, &sectiondm);
2261        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2262        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2263        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2264        PetscSectionSetChart(section, pStart, pEnd);
2265        PetscSectionSetUp(section);
2266        DMSetLocalSection(sectiondm, section);
2267        DMGetLocalVector(sectiondm, &vec);
2268        PetscObjectSetName((PetscObject)vec, "vec_name");
2269        DMPlexTopologyView(dm, viewer);
2270        DMPlexSectionView(dm, viewer, sectiondm);
2271        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2272        DMRestoreLocalVector(sectiondm, &vec);
2273        DMDestroy(&sectiondm);
2274        DMDestroy(&dm);
2275 .ve
2276 
2277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2278 @*/
2279 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2280 {
2281   PetscBool ishdf5;
2282 
2283   PetscFunctionBegin;
2284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2285   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2286   if (!sectiondm) sectiondm = dm;
2287   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2288   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2289   /* Check consistency */
2290   {
2291     PetscSection section;
2292     PetscBool    includesConstraints;
2293     PetscInt     m, m1;
2294 
2295     PetscCall(VecGetLocalSize(vec, &m1));
2296     PetscCall(DMGetLocalSection(sectiondm, &section));
2297     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2298     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2299     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2300     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2301   }
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2304   if (ishdf5) {
2305 #if defined(PETSC_HAVE_HDF5)
2306     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2307 #else
2308     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2309 #endif
2310   }
2311   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2312   PetscFunctionReturn(PETSC_SUCCESS);
2313 }
2314 
2315 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2316 {
2317   PetscBool ishdf5;
2318 
2319   PetscFunctionBegin;
2320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2321   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2322   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2323   if (ishdf5) {
2324 #if defined(PETSC_HAVE_HDF5)
2325     PetscViewerFormat format;
2326     PetscCall(PetscViewerGetFormat(viewer, &format));
2327     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2328       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2329     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2330       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2331     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2332     PetscFunctionReturn(PETSC_SUCCESS);
2333 #else
2334     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2335 #endif
2336   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2337 }
2338 
2339 /*@
2340   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2341 
2342   Collective
2343 
2344   Input Parameters:
2345 + dm     - The `DM` into which the topology is loaded
2346 - viewer - The `PetscViewer` for the saved topology
2347 
2348   Output Parameter:
2349 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2350 
2351   Level: advanced
2352 
2353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2354           `PetscViewer`, `PetscSF`
2355 @*/
2356 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2357 {
2358   PetscBool ishdf5;
2359 
2360   PetscFunctionBegin;
2361   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2362   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2363   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2364   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2365   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2366   if (ishdf5) {
2367 #if defined(PETSC_HAVE_HDF5)
2368     PetscViewerFormat format;
2369     PetscCall(PetscViewerGetFormat(viewer, &format));
2370     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2371       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2372     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2373 #else
2374     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2375 #endif
2376   }
2377   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2378   PetscFunctionReturn(PETSC_SUCCESS);
2379 }
2380 
2381 /*@
2382   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2383 
2384   Collective
2385 
2386   Input Parameters:
2387 + dm                   - The `DM` into which the coordinates are loaded
2388 . viewer               - The `PetscViewer` for the saved coordinates
2389 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2390 
2391   Level: advanced
2392 
2393 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2394           `PetscSF`, `PetscViewer`
2395 @*/
2396 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2397 {
2398   PetscBool ishdf5;
2399 
2400   PetscFunctionBegin;
2401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2402   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2403   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2404   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2405   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2406   if (ishdf5) {
2407 #if defined(PETSC_HAVE_HDF5)
2408     PetscViewerFormat format;
2409     PetscCall(PetscViewerGetFormat(viewer, &format));
2410     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2411       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2412     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2413 #else
2414     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2415 #endif
2416   }
2417   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2418   PetscFunctionReturn(PETSC_SUCCESS);
2419 }
2420 
2421 /*@
2422   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2423 
2424   Collective
2425 
2426   Input Parameters:
2427 + dm                   - The `DM` into which the labels are loaded
2428 . viewer               - The `PetscViewer` for the saved labels
2429 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2430 
2431   Level: advanced
2432 
2433   Note:
2434   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2435 
2436 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2437           `PetscSF`, `PetscViewer`
2438 @*/
2439 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2440 {
2441   PetscBool ishdf5;
2442 
2443   PetscFunctionBegin;
2444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2445   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2446   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2447   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2448   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2449   if (ishdf5) {
2450 #if defined(PETSC_HAVE_HDF5)
2451     PetscViewerFormat format;
2452 
2453     PetscCall(PetscViewerGetFormat(viewer, &format));
2454     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2455       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2456     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2457 #else
2458     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2459 #endif
2460   }
2461   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2462   PetscFunctionReturn(PETSC_SUCCESS);
2463 }
2464 
2465 /*@
2466   DMPlexSectionLoad - Loads section into a `DMPLEX`
2467 
2468   Collective
2469 
2470   Input Parameters:
2471 + dm                   - The `DM` that represents the topology
2472 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2473 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2474 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2475 
2476   Output Parameters:
2477 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2478 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2479 
2480   Level: advanced
2481 
2482   Notes:
2483   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2484 
2485   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2486 
2487   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2488 
2489   Example using 2 processes:
2490 .vb
2491   NX (number of points on dm): 4
2492   sectionA                   : the on-disk section
2493   vecA                       : a vector associated with sectionA
2494   sectionB                   : sectiondm's local section constructed in this function
2495   vecB (local)               : a vector associated with sectiondm's local section
2496   vecB (global)              : a vector associated with sectiondm's global section
2497 
2498                                      rank 0    rank 1
2499   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2500   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2501   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2502   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2503   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2504   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2505   sectionB->atlasDof             :     1 0 1 | 1 3
2506   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2507   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2508   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2509 .ve
2510   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2511 
2512 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2513 @*/
2514 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2515 {
2516   PetscBool ishdf5;
2517 
2518   PetscFunctionBegin;
2519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2520   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2521   if (!sectiondm) sectiondm = dm;
2522   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2523   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2524   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2525   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2527   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2528   if (ishdf5) {
2529 #if defined(PETSC_HAVE_HDF5)
2530     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2531 #else
2532     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2533 #endif
2534   }
2535   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2536   PetscFunctionReturn(PETSC_SUCCESS);
2537 }
2538 
2539 /*@
2540   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2541 
2542   Collective
2543 
2544   Input Parameters:
2545 + dm        - The `DM` that represents the topology
2546 . viewer    - The `PetscViewer` that represents the on-disk vector data
2547 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2548 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2549 - vec       - The global vector to set values of
2550 
2551   Level: advanced
2552 
2553   Notes:
2554   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2555 
2556   Calling sequence:
2557 .vb
2558        DMCreate(PETSC_COMM_WORLD, &dm);
2559        DMSetType(dm, DMPLEX);
2560        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2561        DMPlexTopologyLoad(dm, viewer, &sfX);
2562        DMClone(dm, &sectiondm);
2563        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2564        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2565        DMGetGlobalVector(sectiondm, &vec);
2566        PetscObjectSetName((PetscObject)vec, "vec_name");
2567        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2568        DMRestoreGlobalVector(sectiondm, &vec);
2569        PetscSFDestroy(&gsf);
2570        PetscSFDestroy(&sfX);
2571        DMDestroy(&sectiondm);
2572        DMDestroy(&dm);
2573 .ve
2574 
2575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2576           `PetscSF`, `PetscViewer`
2577 @*/
2578 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2579 {
2580   PetscBool ishdf5;
2581 
2582   PetscFunctionBegin;
2583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2584   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2585   if (!sectiondm) sectiondm = dm;
2586   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2587   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2588   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2589   /* Check consistency */
2590   {
2591     PetscSection section;
2592     PetscBool    includesConstraints;
2593     PetscInt     m, m1;
2594 
2595     PetscCall(VecGetLocalSize(vec, &m1));
2596     PetscCall(DMGetGlobalSection(sectiondm, &section));
2597     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2598     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2599     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2600     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2601   }
2602   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2603   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2604   if (ishdf5) {
2605 #if defined(PETSC_HAVE_HDF5)
2606     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2607 #else
2608     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2609 #endif
2610   }
2611   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2612   PetscFunctionReturn(PETSC_SUCCESS);
2613 }
2614 
2615 /*@
2616   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2617 
2618   Collective
2619 
2620   Input Parameters:
2621 + dm        - The `DM` that represents the topology
2622 . viewer    - The `PetscViewer` that represents the on-disk vector data
2623 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2624 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2625 - vec       - The local vector to set values of
2626 
2627   Level: advanced
2628 
2629   Notes:
2630   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2631 
2632   Calling sequence:
2633 .vb
2634        DMCreate(PETSC_COMM_WORLD, &dm);
2635        DMSetType(dm, DMPLEX);
2636        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2637        DMPlexTopologyLoad(dm, viewer, &sfX);
2638        DMClone(dm, &sectiondm);
2639        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2640        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2641        DMGetLocalVector(sectiondm, &vec);
2642        PetscObjectSetName((PetscObject)vec, "vec_name");
2643        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2644        DMRestoreLocalVector(sectiondm, &vec);
2645        PetscSFDestroy(&lsf);
2646        PetscSFDestroy(&sfX);
2647        DMDestroy(&sectiondm);
2648        DMDestroy(&dm);
2649 .ve
2650 
2651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2652           `PetscSF`, `PetscViewer`
2653 @*/
2654 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2655 {
2656   PetscBool ishdf5;
2657 
2658   PetscFunctionBegin;
2659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2660   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2661   if (!sectiondm) sectiondm = dm;
2662   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2663   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2664   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2665   /* Check consistency */
2666   {
2667     PetscSection section;
2668     PetscBool    includesConstraints;
2669     PetscInt     m, m1;
2670 
2671     PetscCall(VecGetLocalSize(vec, &m1));
2672     PetscCall(DMGetLocalSection(sectiondm, &section));
2673     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2674     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2675     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2676     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2677   }
2678   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2679   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2680   if (ishdf5) {
2681 #if defined(PETSC_HAVE_HDF5)
2682     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2683 #else
2684     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2685 #endif
2686   }
2687   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2688   PetscFunctionReturn(PETSC_SUCCESS);
2689 }
2690 
2691 PetscErrorCode DMDestroy_Plex(DM dm)
2692 {
2693   DM_Plex *mesh = (DM_Plex *)dm->data;
2694 
2695   PetscFunctionBegin;
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2714   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2716   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2717   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2718   PetscCall(PetscFree(mesh->cones));
2719   PetscCall(PetscFree(mesh->coneOrientations));
2720   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2721   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2722   PetscCall(PetscFree(mesh->supports));
2723   PetscCall(PetscFree(mesh->cellTypes));
2724   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2725   PetscCall(PetscFree(mesh->tetgenOpts));
2726   PetscCall(PetscFree(mesh->triangleOpts));
2727   PetscCall(PetscFree(mesh->transformType));
2728   PetscCall(PetscFree(mesh->distributionName));
2729   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2730   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2731   PetscCall(ISDestroy(&mesh->subpointIS));
2732   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2733   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2734   if (mesh->periodic.face_sfs) {
2735     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2736     PetscCall(PetscFree(mesh->periodic.face_sfs));
2737   }
2738   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2739   if (mesh->periodic.periodic_points) {
2740     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2741     PetscCall(PetscFree(mesh->periodic.periodic_points));
2742   }
2743   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2744   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2745   PetscCall(ISDestroy(&mesh->anchorIS));
2746   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2747   PetscCall(PetscFree(mesh->parents));
2748   PetscCall(PetscFree(mesh->childIDs));
2749   PetscCall(PetscSectionDestroy(&mesh->childSection));
2750   PetscCall(PetscFree(mesh->children));
2751   PetscCall(DMDestroy(&mesh->referenceTree));
2752   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2753   PetscCall(PetscFree(mesh->neighbors));
2754   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2755   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2756   PetscCall(PetscFree(mesh));
2757   PetscFunctionReturn(PETSC_SUCCESS);
2758 }
2759 
2760 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2761 {
2762   PetscSection           sectionGlobal, sectionLocal;
2763   PetscInt               bs = -1, mbs;
2764   PetscInt               localSize, localStart = 0;
2765   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2766   MatType                mtype;
2767   ISLocalToGlobalMapping ltog;
2768 
2769   PetscFunctionBegin;
2770   PetscCall(MatInitializePackage());
2771   mtype = dm->mattype;
2772   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2773   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2774   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2775   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2776   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2777   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2778   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2779   PetscCall(MatSetType(*J, mtype));
2780   PetscCall(MatSetFromOptions(*J));
2781   PetscCall(MatGetBlockSize(*J, &mbs));
2782   if (mbs > 1) bs = mbs;
2783   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2784   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2785   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2786   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2787   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2788   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2789   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2790   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2791   if (!isShell) {
2792     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2793     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2794     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2795 
2796     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2797 
2798     PetscCall(PetscCalloc1(localSize, &pblocks));
2799     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2800     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2801     for (p = pStart; p < pEnd; ++p) {
2802       switch (dm->blocking_type) {
2803       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2804         PetscInt bdof, offset;
2805 
2806         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2807         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2808         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2809         if (dof > 0) {
2810           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2811           // Signal block concatenation
2812           if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2813         }
2814         dof  = dof < 0 ? -(dof + 1) : dof;
2815         bdof = cdof && (dof - cdof) ? 1 : dof;
2816         if (dof) {
2817           if (bs < 0) {
2818             bs = bdof;
2819           } else if (bs != bdof) {
2820             bs = 1;
2821           }
2822         }
2823       } break;
2824       case DM_BLOCKING_FIELD_NODE: {
2825         for (PetscInt field = 0; field < num_fields; field++) {
2826           PetscInt num_comp, bdof, offset;
2827           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2828           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2829           if (dof < 0) continue;
2830           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2831           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2832           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);
2833           PetscInt num_nodes = dof / num_comp;
2834           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2835           // Handle possibly constant block size (unlikely)
2836           bdof = cdof && (dof - cdof) ? 1 : dof;
2837           if (dof) {
2838             if (bs < 0) {
2839               bs = bdof;
2840             } else if (bs != bdof) {
2841               bs = 1;
2842             }
2843           }
2844         }
2845       } break;
2846       }
2847     }
2848     /* Must have same blocksize on all procs (some might have no points) */
2849     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2850     bsLocal[1] = bs;
2851     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2852     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2853     else bs = bsMinMax[0];
2854     bs = PetscMax(1, bs);
2855     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2856     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2857       PetscCall(MatSetBlockSize(*J, bs));
2858       PetscCall(MatSetUp(*J));
2859     } else {
2860       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2861       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2862       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2863     }
2864     if (pblocks) { // Consolidate blocks
2865       PetscInt nblocks = 0;
2866       pblocks[0]       = PetscAbs(pblocks[0]);
2867       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2868         if (pblocks[i] == 0) continue;
2869         // Negative block size indicates the blocks should be concatenated
2870         if (pblocks[i] < 0) {
2871           pblocks[i] = -pblocks[i];
2872           pblocks[nblocks - 1] += pblocks[i];
2873         } else {
2874           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2875         }
2876         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]);
2877       }
2878       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2879     }
2880     PetscCall(PetscFree(pblocks));
2881   }
2882   PetscCall(MatSetDM(*J, dm));
2883   PetscFunctionReturn(PETSC_SUCCESS);
2884 }
2885 
2886 /*@
2887   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2888 
2889   Not Collective
2890 
2891   Input Parameter:
2892 . dm - The `DMPLEX`
2893 
2894   Output Parameter:
2895 . subsection - The subdomain section
2896 
2897   Level: developer
2898 
2899 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2900 @*/
2901 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2902 {
2903   DM_Plex *mesh = (DM_Plex *)dm->data;
2904 
2905   PetscFunctionBegin;
2906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2907   if (!mesh->subdomainSection) {
2908     PetscSection section;
2909     PetscSF      sf;
2910 
2911     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2912     PetscCall(DMGetLocalSection(dm, &section));
2913     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2914     PetscCall(PetscSFDestroy(&sf));
2915   }
2916   *subsection = mesh->subdomainSection;
2917   PetscFunctionReturn(PETSC_SUCCESS);
2918 }
2919 
2920 /*@
2921   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2922 
2923   Not Collective
2924 
2925   Input Parameter:
2926 . dm - The `DMPLEX`
2927 
2928   Output Parameters:
2929 + pStart - The first mesh point
2930 - pEnd   - The upper bound for mesh points
2931 
2932   Level: beginner
2933 
2934 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2935 @*/
2936 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2937 {
2938   DM_Plex *mesh = (DM_Plex *)dm->data;
2939 
2940   PetscFunctionBegin;
2941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2942   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2943   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2944   PetscFunctionReturn(PETSC_SUCCESS);
2945 }
2946 
2947 /*@
2948   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2949 
2950   Not Collective
2951 
2952   Input Parameters:
2953 + dm     - The `DMPLEX`
2954 . pStart - The first mesh point
2955 - pEnd   - The upper bound for mesh points
2956 
2957   Level: beginner
2958 
2959 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2960 @*/
2961 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2962 {
2963   DM_Plex *mesh = (DM_Plex *)dm->data;
2964 
2965   PetscFunctionBegin;
2966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2967   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2968   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2969   PetscCall(PetscFree(mesh->cellTypes));
2970   PetscFunctionReturn(PETSC_SUCCESS);
2971 }
2972 
2973 /*@
2974   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2975 
2976   Not Collective
2977 
2978   Input Parameters:
2979 + dm - The `DMPLEX`
2980 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2981 
2982   Output Parameter:
2983 . size - The cone size for point `p`
2984 
2985   Level: beginner
2986 
2987 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2988 @*/
2989 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2990 {
2991   DM_Plex *mesh = (DM_Plex *)dm->data;
2992 
2993   PetscFunctionBegin;
2994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2995   PetscAssertPointer(size, 3);
2996   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2997   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2998   PetscFunctionReturn(PETSC_SUCCESS);
2999 }
3000 
3001 /*@
3002   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3003 
3004   Not Collective
3005 
3006   Input Parameters:
3007 + dm   - The `DMPLEX`
3008 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3009 - size - The cone size for point `p`
3010 
3011   Level: beginner
3012 
3013   Note:
3014   This should be called after `DMPlexSetChart()`.
3015 
3016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3017 @*/
3018 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3019 {
3020   DM_Plex *mesh = (DM_Plex *)dm->data;
3021 
3022   PetscFunctionBegin;
3023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3024   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3025   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3026   PetscFunctionReturn(PETSC_SUCCESS);
3027 }
3028 
3029 /*@C
3030   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3031 
3032   Not Collective
3033 
3034   Input Parameters:
3035 + dm - The `DMPLEX`
3036 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3037 
3038   Output Parameter:
3039 . cone - An array of points which are on the in-edges for point `p`
3040 
3041   Level: beginner
3042 
3043   Fortran Notes:
3044   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3045   `DMPlexRestoreCone()` is not needed/available in C.
3046 
3047 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3048 @*/
3049 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3050 {
3051   DM_Plex *mesh = (DM_Plex *)dm->data;
3052   PetscInt off;
3053 
3054   PetscFunctionBegin;
3055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3056   PetscAssertPointer(cone, 3);
3057   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3058   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3059   PetscFunctionReturn(PETSC_SUCCESS);
3060 }
3061 
3062 /*@C
3063   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3064 
3065   Not Collective
3066 
3067   Input Parameters:
3068 + dm - The `DMPLEX`
3069 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3070 
3071   Output Parameters:
3072 + pConesSection - `PetscSection` describing the layout of `pCones`
3073 - pCones        - An array of points which are on the in-edges for the point set `p`
3074 
3075   Level: intermediate
3076 
3077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3078 @*/
3079 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3080 {
3081   PetscSection cs, newcs;
3082   PetscInt    *cones;
3083   PetscInt    *newarr = NULL;
3084   PetscInt     n;
3085 
3086   PetscFunctionBegin;
3087   PetscCall(DMPlexGetCones(dm, &cones));
3088   PetscCall(DMPlexGetConeSection(dm, &cs));
3089   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3090   if (pConesSection) *pConesSection = newcs;
3091   if (pCones) {
3092     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3093     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3094   }
3095   PetscFunctionReturn(PETSC_SUCCESS);
3096 }
3097 
3098 /*@
3099   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3100 
3101   Not Collective
3102 
3103   Input Parameters:
3104 + dm     - The `DMPLEX`
3105 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3106 
3107   Output Parameter:
3108 . expandedPoints - An array of vertices recursively expanded from input points
3109 
3110   Level: advanced
3111 
3112   Notes:
3113   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3114 
3115   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3116 
3117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3118           `DMPlexGetDepth()`, `IS`
3119 @*/
3120 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3121 {
3122   IS      *expandedPointsAll;
3123   PetscInt depth;
3124 
3125   PetscFunctionBegin;
3126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3127   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3128   PetscAssertPointer(expandedPoints, 3);
3129   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3130   *expandedPoints = expandedPointsAll[0];
3131   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3132   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3133   PetscFunctionReturn(PETSC_SUCCESS);
3134 }
3135 
3136 /*@
3137   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).
3138 
3139   Not Collective
3140 
3141   Input Parameters:
3142 + dm     - The `DMPLEX`
3143 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3144 
3145   Output Parameters:
3146 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3147 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3148 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3149 
3150   Level: advanced
3151 
3152   Notes:
3153   Like `DMPlexGetConeTuple()` but recursive.
3154 
3155   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.
3156   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3157 
3158   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\:
3159   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3160   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3161 
3162 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3163           `DMPlexGetDepth()`, `PetscSection`, `IS`
3164 @*/
3165 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3166 {
3167   const PetscInt *arr0 = NULL, *cone = NULL;
3168   PetscInt       *arr = NULL, *newarr = NULL;
3169   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3170   IS             *expandedPoints_;
3171   PetscSection   *sections_;
3172 
3173   PetscFunctionBegin;
3174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3175   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3176   if (depth) PetscAssertPointer(depth, 3);
3177   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3178   if (sections) PetscAssertPointer(sections, 5);
3179   PetscCall(ISGetLocalSize(points, &n));
3180   PetscCall(ISGetIndices(points, &arr0));
3181   PetscCall(DMPlexGetDepth(dm, &depth_));
3182   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3183   PetscCall(PetscCalloc1(depth_, &sections_));
3184   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3185   for (d = depth_ - 1; d >= 0; d--) {
3186     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3187     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3188     for (i = 0; i < n; i++) {
3189       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3190       if (arr[i] >= start && arr[i] < end) {
3191         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3192         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3193       } else {
3194         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3195       }
3196     }
3197     PetscCall(PetscSectionSetUp(sections_[d]));
3198     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3199     PetscCall(PetscMalloc1(newn, &newarr));
3200     for (i = 0; i < n; i++) {
3201       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3202       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3203       if (cn > 1) {
3204         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3205         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3206       } else {
3207         newarr[co] = arr[i];
3208       }
3209     }
3210     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3211     arr = newarr;
3212     n   = newn;
3213   }
3214   PetscCall(ISRestoreIndices(points, &arr0));
3215   *depth = depth_;
3216   if (expandedPoints) *expandedPoints = expandedPoints_;
3217   else {
3218     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3219     PetscCall(PetscFree(expandedPoints_));
3220   }
3221   if (sections) *sections = sections_;
3222   else {
3223     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3224     PetscCall(PetscFree(sections_));
3225   }
3226   PetscFunctionReturn(PETSC_SUCCESS);
3227 }
3228 
3229 /*@
3230   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3231 
3232   Not Collective
3233 
3234   Input Parameters:
3235 + dm     - The `DMPLEX`
3236 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3237 
3238   Output Parameters:
3239 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3240 . expandedPoints - (optional) An array of recursively expanded cones
3241 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3242 
3243   Level: advanced
3244 
3245   Note:
3246   See `DMPlexGetConeRecursive()`
3247 
3248 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3249           `DMPlexGetDepth()`, `IS`, `PetscSection`
3250 @*/
3251 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3252 {
3253   PetscInt d, depth_;
3254 
3255   PetscFunctionBegin;
3256   PetscCall(DMPlexGetDepth(dm, &depth_));
3257   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3258   if (depth) *depth = 0;
3259   if (expandedPoints) {
3260     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3261     PetscCall(PetscFree(*expandedPoints));
3262   }
3263   if (sections) {
3264     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3265     PetscCall(PetscFree(*sections));
3266   }
3267   PetscFunctionReturn(PETSC_SUCCESS);
3268 }
3269 
3270 /*@
3271   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
3272 
3273   Not Collective
3274 
3275   Input Parameters:
3276 + dm   - The `DMPLEX`
3277 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3278 - cone - An array of points which are on the in-edges for point `p`
3279 
3280   Level: beginner
3281 
3282   Note:
3283   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3284 
3285 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3286 @*/
3287 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3288 {
3289   DM_Plex *mesh = (DM_Plex *)dm->data;
3290   PetscInt dof, off, c;
3291 
3292   PetscFunctionBegin;
3293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3294   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3295   if (dof) PetscAssertPointer(cone, 3);
3296   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3297   if (PetscDefined(USE_DEBUG)) {
3298     PetscInt pStart, pEnd;
3299     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3300     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);
3301     for (c = 0; c < dof; ++c) {
3302       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);
3303       mesh->cones[off + c] = cone[c];
3304     }
3305   } else {
3306     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3307   }
3308   PetscFunctionReturn(PETSC_SUCCESS);
3309 }
3310 
3311 /*@C
3312   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3313 
3314   Not Collective
3315 
3316   Input Parameters:
3317 + dm - The `DMPLEX`
3318 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3319 
3320   Output Parameter:
3321 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3322                     integer giving the prescription for cone traversal.
3323 
3324   Level: beginner
3325 
3326   Note:
3327   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3328   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3329   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3330   with the identity.
3331 
3332   Fortran Notes:
3333   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3334   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3335 
3336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3337 @*/
3338 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3339 {
3340   DM_Plex *mesh = (DM_Plex *)dm->data;
3341   PetscInt off;
3342 
3343   PetscFunctionBegin;
3344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3345   if (PetscDefined(USE_DEBUG)) {
3346     PetscInt dof;
3347     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3348     if (dof) PetscAssertPointer(coneOrientation, 3);
3349   }
3350   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3351 
3352   *coneOrientation = &mesh->coneOrientations[off];
3353   PetscFunctionReturn(PETSC_SUCCESS);
3354 }
3355 
3356 /*@
3357   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3358 
3359   Not Collective
3360 
3361   Input Parameters:
3362 + dm              - The `DMPLEX`
3363 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3364 - coneOrientation - An array of orientations
3365 
3366   Level: beginner
3367 
3368   Notes:
3369   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3370 
3371   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3372 
3373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3374 @*/
3375 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3376 {
3377   DM_Plex *mesh = (DM_Plex *)dm->data;
3378   PetscInt pStart, pEnd;
3379   PetscInt dof, off, c;
3380 
3381   PetscFunctionBegin;
3382   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3383   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3384   if (dof) PetscAssertPointer(coneOrientation, 3);
3385   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3386   if (PetscDefined(USE_DEBUG)) {
3387     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3388     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);
3389     for (c = 0; c < dof; ++c) {
3390       PetscInt cdof, o = coneOrientation[c];
3391 
3392       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3393       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);
3394       mesh->coneOrientations[off + c] = o;
3395     }
3396   } else {
3397     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3398   }
3399   PetscFunctionReturn(PETSC_SUCCESS);
3400 }
3401 
3402 /*@
3403   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3404 
3405   Not Collective
3406 
3407   Input Parameters:
3408 + dm        - The `DMPLEX`
3409 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3410 . conePos   - The local index in the cone where the point should be put
3411 - conePoint - The mesh point to insert
3412 
3413   Level: beginner
3414 
3415 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3416 @*/
3417 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3418 {
3419   DM_Plex *mesh = (DM_Plex *)dm->data;
3420   PetscInt pStart, pEnd;
3421   PetscInt dof, off;
3422 
3423   PetscFunctionBegin;
3424   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3425   if (PetscDefined(USE_DEBUG)) {
3426     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3427     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);
3428     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);
3429     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3430     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);
3431   }
3432   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3433   mesh->cones[off + conePos] = conePoint;
3434   PetscFunctionReturn(PETSC_SUCCESS);
3435 }
3436 
3437 /*@
3438   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3439 
3440   Not Collective
3441 
3442   Input Parameters:
3443 + dm              - The `DMPLEX`
3444 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3445 . conePos         - The local index in the cone where the point should be put
3446 - coneOrientation - The point orientation to insert
3447 
3448   Level: beginner
3449 
3450   Note:
3451   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3452 
3453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3454 @*/
3455 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3456 {
3457   DM_Plex *mesh = (DM_Plex *)dm->data;
3458   PetscInt pStart, pEnd;
3459   PetscInt dof, off;
3460 
3461   PetscFunctionBegin;
3462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3463   if (PetscDefined(USE_DEBUG)) {
3464     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3465     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);
3466     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3467     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);
3468   }
3469   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3470   mesh->coneOrientations[off + conePos] = coneOrientation;
3471   PetscFunctionReturn(PETSC_SUCCESS);
3472 }
3473 
3474 /*@C
3475   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3476 
3477   Not collective
3478 
3479   Input Parameters:
3480 + dm - The DMPlex
3481 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3482 
3483   Output Parameters:
3484 + cone - An array of points which are on the in-edges for point `p`
3485 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3486         integer giving the prescription for cone traversal.
3487 
3488   Level: beginner
3489 
3490   Notes:
3491   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3492   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3493   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3494   with the identity.
3495 
3496   Fortran Notes:
3497   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3498   `DMPlexRestoreCone()` is not needed/available in C.
3499 
3500 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3501 @*/
3502 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3503 {
3504   DM_Plex *mesh = (DM_Plex *)dm->data;
3505 
3506   PetscFunctionBegin;
3507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3508   if (mesh->tr) {
3509     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3510   } else {
3511     PetscInt off;
3512     if (PetscDefined(USE_DEBUG)) {
3513       PetscInt dof;
3514       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3515       if (dof) {
3516         if (cone) PetscAssertPointer(cone, 3);
3517         if (ornt) PetscAssertPointer(ornt, 4);
3518       }
3519     }
3520     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3521     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3522     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3523   }
3524   PetscFunctionReturn(PETSC_SUCCESS);
3525 }
3526 
3527 /*@C
3528   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3529 
3530   Not Collective
3531 
3532   Input Parameters:
3533 + dm   - The DMPlex
3534 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3535 . cone - An array of points which are on the in-edges for point p
3536 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3537         integer giving the prescription for cone traversal.
3538 
3539   Level: beginner
3540 
3541   Notes:
3542   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3543   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3544   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3545   with the identity.
3546 
3547   Fortran Notes:
3548   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3549   `DMPlexRestoreCone()` is not needed/available in C.
3550 
3551 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3552 @*/
3553 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3554 {
3555   DM_Plex *mesh = (DM_Plex *)dm->data;
3556 
3557   PetscFunctionBegin;
3558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3559   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3560   PetscFunctionReturn(PETSC_SUCCESS);
3561 }
3562 
3563 /*@
3564   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3565 
3566   Not Collective
3567 
3568   Input Parameters:
3569 + dm - The `DMPLEX`
3570 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3571 
3572   Output Parameter:
3573 . size - The support size for point `p`
3574 
3575   Level: beginner
3576 
3577 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3578 @*/
3579 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3580 {
3581   DM_Plex *mesh = (DM_Plex *)dm->data;
3582 
3583   PetscFunctionBegin;
3584   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3585   PetscAssertPointer(size, 3);
3586   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3587   PetscFunctionReturn(PETSC_SUCCESS);
3588 }
3589 
3590 /*@
3591   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3592 
3593   Not Collective
3594 
3595   Input Parameters:
3596 + dm   - The `DMPLEX`
3597 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3598 - size - The support size for point `p`
3599 
3600   Level: beginner
3601 
3602   Note:
3603   This should be called after `DMPlexSetChart()`.
3604 
3605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3606 @*/
3607 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3608 {
3609   DM_Plex *mesh = (DM_Plex *)dm->data;
3610 
3611   PetscFunctionBegin;
3612   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3613   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3614   PetscFunctionReturn(PETSC_SUCCESS);
3615 }
3616 
3617 /*@C
3618   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3619 
3620   Not Collective
3621 
3622   Input Parameters:
3623 + dm - The `DMPLEX`
3624 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3625 
3626   Output Parameter:
3627 . support - An array of points which are on the out-edges for point `p`
3628 
3629   Level: beginner
3630 
3631   Fortran Notes:
3632   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3633   `DMPlexRestoreSupport()` is not needed/available in C.
3634 
3635 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3636 @*/
3637 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3638 {
3639   DM_Plex *mesh = (DM_Plex *)dm->data;
3640   PetscInt off;
3641 
3642   PetscFunctionBegin;
3643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3644   PetscAssertPointer(support, 3);
3645   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3646   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3647   PetscFunctionReturn(PETSC_SUCCESS);
3648 }
3649 
3650 /*@
3651   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3652 
3653   Not Collective
3654 
3655   Input Parameters:
3656 + dm      - The `DMPLEX`
3657 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3658 - support - An array of points which are on the out-edges for point `p`
3659 
3660   Level: beginner
3661 
3662   Note:
3663   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3664 
3665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3666 @*/
3667 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3668 {
3669   DM_Plex *mesh = (DM_Plex *)dm->data;
3670   PetscInt pStart, pEnd;
3671   PetscInt dof, off, c;
3672 
3673   PetscFunctionBegin;
3674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3675   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3676   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3677   if (dof) PetscAssertPointer(support, 3);
3678   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3679   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);
3680   for (c = 0; c < dof; ++c) {
3681     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);
3682     mesh->supports[off + c] = support[c];
3683   }
3684   PetscFunctionReturn(PETSC_SUCCESS);
3685 }
3686 
3687 /*@
3688   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3689 
3690   Not Collective
3691 
3692   Input Parameters:
3693 + dm           - The `DMPLEX`
3694 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3695 . supportPos   - The local index in the cone where the point should be put
3696 - supportPoint - The mesh point to insert
3697 
3698   Level: beginner
3699 
3700 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3701 @*/
3702 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3703 {
3704   DM_Plex *mesh = (DM_Plex *)dm->data;
3705   PetscInt pStart, pEnd;
3706   PetscInt dof, off;
3707 
3708   PetscFunctionBegin;
3709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3710   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3711   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3712   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3713   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);
3714   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);
3715   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);
3716   mesh->supports[off + supportPos] = supportPoint;
3717   PetscFunctionReturn(PETSC_SUCCESS);
3718 }
3719 
3720 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3721 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3722 {
3723   switch (ct) {
3724   case DM_POLYTOPE_SEGMENT:
3725     if (o == -1) return -2;
3726     break;
3727   case DM_POLYTOPE_TRIANGLE:
3728     if (o == -3) return -1;
3729     if (o == -2) return -3;
3730     if (o == -1) return -2;
3731     break;
3732   case DM_POLYTOPE_QUADRILATERAL:
3733     if (o == -4) return -2;
3734     if (o == -3) return -1;
3735     if (o == -2) return -4;
3736     if (o == -1) return -3;
3737     break;
3738   default:
3739     return o;
3740   }
3741   return o;
3742 }
3743 
3744 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3745 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3746 {
3747   switch (ct) {
3748   case DM_POLYTOPE_SEGMENT:
3749     if ((o == -2) || (o == 1)) return -1;
3750     if (o == -1) return 0;
3751     break;
3752   case DM_POLYTOPE_TRIANGLE:
3753     if (o == -3) return -2;
3754     if (o == -2) return -1;
3755     if (o == -1) return -3;
3756     break;
3757   case DM_POLYTOPE_QUADRILATERAL:
3758     if (o == -4) return -2;
3759     if (o == -3) return -1;
3760     if (o == -2) return -4;
3761     if (o == -1) return -3;
3762     break;
3763   default:
3764     return o;
3765   }
3766   return o;
3767 }
3768 
3769 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3770 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3771 {
3772   PetscInt pStart, pEnd, p;
3773 
3774   PetscFunctionBegin;
3775   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3776   for (p = pStart; p < pEnd; ++p) {
3777     const PetscInt *cone, *ornt;
3778     PetscInt        coneSize, c;
3779 
3780     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3781     PetscCall(DMPlexGetCone(dm, p, &cone));
3782     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3783     for (c = 0; c < coneSize; ++c) {
3784       DMPolytopeType ct;
3785       const PetscInt o = ornt[c];
3786 
3787       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3788       switch (ct) {
3789       case DM_POLYTOPE_SEGMENT:
3790         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3791         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3792         break;
3793       case DM_POLYTOPE_TRIANGLE:
3794         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3795         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3796         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3797         break;
3798       case DM_POLYTOPE_QUADRILATERAL:
3799         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3800         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3801         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3802         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3803         break;
3804       default:
3805         break;
3806       }
3807     }
3808   }
3809   PetscFunctionReturn(PETSC_SUCCESS);
3810 }
3811 
3812 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3813 {
3814   DM_Plex *mesh = (DM_Plex *)dm->data;
3815 
3816   PetscFunctionBeginHot;
3817   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3818     if (useCone) {
3819       PetscCall(DMPlexGetConeSize(dm, p, size));
3820       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3821     } else {
3822       PetscCall(DMPlexGetSupportSize(dm, p, size));
3823       PetscCall(DMPlexGetSupport(dm, p, arr));
3824     }
3825   } else {
3826     if (useCone) {
3827       const PetscSection s   = mesh->coneSection;
3828       const PetscInt     ps  = p - s->pStart;
3829       const PetscInt     off = s->atlasOff[ps];
3830 
3831       *size = s->atlasDof[ps];
3832       *arr  = mesh->cones + off;
3833       *ornt = mesh->coneOrientations + off;
3834     } else {
3835       const PetscSection s   = mesh->supportSection;
3836       const PetscInt     ps  = p - s->pStart;
3837       const PetscInt     off = s->atlasOff[ps];
3838 
3839       *size = s->atlasDof[ps];
3840       *arr  = mesh->supports + off;
3841     }
3842   }
3843   PetscFunctionReturn(PETSC_SUCCESS);
3844 }
3845 
3846 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3847 {
3848   DM_Plex *mesh = (DM_Plex *)dm->data;
3849 
3850   PetscFunctionBeginHot;
3851   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3852     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3853   }
3854   PetscFunctionReturn(PETSC_SUCCESS);
3855 }
3856 
3857 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3858 {
3859   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3860   PetscInt       *closure;
3861   const PetscInt *tmp = NULL, *tmpO = NULL;
3862   PetscInt        off = 0, tmpSize, t;
3863 
3864   PetscFunctionBeginHot;
3865   if (ornt) {
3866     PetscCall(DMPlexGetCellType(dm, p, &ct));
3867     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;
3868   }
3869   if (*points) {
3870     closure = *points;
3871   } else {
3872     PetscInt maxConeSize, maxSupportSize;
3873     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3874     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3875   }
3876   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3877   if (ct == DM_POLYTOPE_UNKNOWN) {
3878     closure[off++] = p;
3879     closure[off++] = 0;
3880     for (t = 0; t < tmpSize; ++t) {
3881       closure[off++] = tmp[t];
3882       closure[off++] = tmpO ? tmpO[t] : 0;
3883     }
3884   } else {
3885     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3886 
3887     /* We assume that cells with a valid type have faces with a valid type */
3888     closure[off++] = p;
3889     closure[off++] = ornt;
3890     for (t = 0; t < tmpSize; ++t) {
3891       DMPolytopeType ft;
3892 
3893       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3894       closure[off++] = tmp[arr[t]];
3895       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3896     }
3897   }
3898   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3899   if (numPoints) *numPoints = tmpSize + 1;
3900   if (points) *points = closure;
3901   PetscFunctionReturn(PETSC_SUCCESS);
3902 }
3903 
3904 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3905 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3906 {
3907   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3908   const PetscInt *cone, *ornt;
3909   PetscInt       *pts, *closure = NULL;
3910   DMPolytopeType  ft;
3911   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3912   PetscInt        dim, coneSize, c, d, clSize, cl;
3913 
3914   PetscFunctionBeginHot;
3915   PetscCall(DMGetDimension(dm, &dim));
3916   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3917   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3918   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3919   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3920   maxSize       = PetscMax(coneSeries, supportSeries);
3921   if (*points) {
3922     pts = *points;
3923   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3924   c        = 0;
3925   pts[c++] = point;
3926   pts[c++] = o;
3927   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3928   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3929   for (cl = 0; cl < clSize * 2; cl += 2) {
3930     pts[c++] = closure[cl];
3931     pts[c++] = closure[cl + 1];
3932   }
3933   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3934   for (cl = 0; cl < clSize * 2; cl += 2) {
3935     pts[c++] = closure[cl];
3936     pts[c++] = closure[cl + 1];
3937   }
3938   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3939   for (d = 2; d < coneSize; ++d) {
3940     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3941     pts[c++] = cone[arr[d * 2 + 0]];
3942     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3943   }
3944   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3945   if (dim >= 3) {
3946     for (d = 2; d < coneSize; ++d) {
3947       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3948       const PetscInt *fcone, *fornt;
3949       PetscInt        fconeSize, fc, i;
3950 
3951       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3952       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3953       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3954       for (fc = 0; fc < fconeSize; ++fc) {
3955         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3956         const PetscInt co = farr[fc * 2 + 1];
3957 
3958         for (i = 0; i < c; i += 2)
3959           if (pts[i] == cp) break;
3960         if (i == c) {
3961           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3962           pts[c++] = cp;
3963           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3964         }
3965       }
3966       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3967     }
3968   }
3969   *numPoints = c / 2;
3970   *points    = pts;
3971   PetscFunctionReturn(PETSC_SUCCESS);
3972 }
3973 
3974 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3975 {
3976   DMPolytopeType ct;
3977   PetscInt      *closure, *fifo;
3978   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3979   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3980   PetscInt       depth, maxSize;
3981 
3982   PetscFunctionBeginHot;
3983   PetscCall(DMPlexGetDepth(dm, &depth));
3984   if (depth == 1) {
3985     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3986     PetscFunctionReturn(PETSC_SUCCESS);
3987   }
3988   PetscCall(DMPlexGetCellType(dm, p, &ct));
3989   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;
3990   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3991     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3992     PetscFunctionReturn(PETSC_SUCCESS);
3993   }
3994   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3995   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3996   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3997   maxSize       = PetscMax(coneSeries, supportSeries);
3998   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3999   if (*points) {
4000     closure = *points;
4001   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4002   closure[closureSize++] = p;
4003   closure[closureSize++] = ornt;
4004   fifo[fifoSize++]       = p;
4005   fifo[fifoSize++]       = ornt;
4006   fifo[fifoSize++]       = ct;
4007   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4008   while (fifoSize - fifoStart) {
4009     const PetscInt       q    = fifo[fifoStart++];
4010     const PetscInt       o    = fifo[fifoStart++];
4011     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4012     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4013     const PetscInt      *tmp, *tmpO = NULL;
4014     PetscInt             tmpSize, t;
4015 
4016     if (PetscDefined(USE_DEBUG)) {
4017       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4018       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);
4019     }
4020     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4021     for (t = 0; t < tmpSize; ++t) {
4022       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4023       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4024       const PetscInt cp = tmp[ip];
4025       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4026       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4027       PetscInt       c;
4028 
4029       /* Check for duplicate */
4030       for (c = 0; c < closureSize; c += 2) {
4031         if (closure[c] == cp) break;
4032       }
4033       if (c == closureSize) {
4034         closure[closureSize++] = cp;
4035         closure[closureSize++] = co;
4036         fifo[fifoSize++]       = cp;
4037         fifo[fifoSize++]       = co;
4038         fifo[fifoSize++]       = ct;
4039       }
4040     }
4041     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4042   }
4043   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4044   if (numPoints) *numPoints = closureSize / 2;
4045   if (points) *points = closure;
4046   PetscFunctionReturn(PETSC_SUCCESS);
4047 }
4048 
4049 /*@C
4050   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4051 
4052   Not Collective
4053 
4054   Input Parameters:
4055 + dm      - The `DMPLEX`
4056 . p       - The mesh point
4057 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4058 
4059   Input/Output Parameter:
4060 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4061            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4062 
4063   Output Parameter:
4064 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4065 
4066   Level: beginner
4067 
4068   Note:
4069   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4070 
4071   Fortran Notes:
4072   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4073 
4074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4075 @*/
4076 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4077 {
4078   PetscFunctionBeginHot;
4079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4080   if (numPoints) PetscAssertPointer(numPoints, 4);
4081   if (points) PetscAssertPointer(points, 5);
4082   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4083   PetscFunctionReturn(PETSC_SUCCESS);
4084 }
4085 
4086 /*@C
4087   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4088 
4089   Not Collective
4090 
4091   Input Parameters:
4092 + dm        - The `DMPLEX`
4093 . p         - The mesh point
4094 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4095 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4096 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4097 
4098   Level: beginner
4099 
4100   Note:
4101   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4102 
4103 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4104 @*/
4105 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4106 {
4107   PetscFunctionBeginHot;
4108   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4109   if (numPoints) *numPoints = 0;
4110   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4111   PetscFunctionReturn(PETSC_SUCCESS);
4112 }
4113 
4114 /*@
4115   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4116 
4117   Not Collective
4118 
4119   Input Parameter:
4120 . dm - The `DMPLEX`
4121 
4122   Output Parameters:
4123 + maxConeSize    - The maximum number of in-edges
4124 - maxSupportSize - The maximum number of out-edges
4125 
4126   Level: beginner
4127 
4128 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4129 @*/
4130 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4131 {
4132   DM_Plex *mesh = (DM_Plex *)dm->data;
4133 
4134   PetscFunctionBegin;
4135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4136   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4137   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4138   PetscFunctionReturn(PETSC_SUCCESS);
4139 }
4140 
4141 PetscErrorCode DMSetUp_Plex(DM dm)
4142 {
4143   DM_Plex *mesh = (DM_Plex *)dm->data;
4144   PetscInt size, maxSupportSize;
4145 
4146   PetscFunctionBegin;
4147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4148   PetscCall(PetscSectionSetUp(mesh->coneSection));
4149   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4150   PetscCall(PetscMalloc1(size, &mesh->cones));
4151   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4152   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4153   if (maxSupportSize) {
4154     PetscCall(PetscSectionSetUp(mesh->supportSection));
4155     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4156     PetscCall(PetscMalloc1(size, &mesh->supports));
4157   }
4158   PetscFunctionReturn(PETSC_SUCCESS);
4159 }
4160 
4161 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4162 {
4163   PetscFunctionBegin;
4164   if (subdm) PetscCall(DMClone(dm, subdm));
4165   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4166   if (subdm) (*subdm)->useNatural = dm->useNatural;
4167   if (dm->useNatural && dm->sfMigration) {
4168     PetscSF sfNatural;
4169 
4170     (*subdm)->sfMigration = dm->sfMigration;
4171     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4172     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4173     (*subdm)->sfNatural = sfNatural;
4174   }
4175   PetscFunctionReturn(PETSC_SUCCESS);
4176 }
4177 
4178 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4179 {
4180   PetscInt i = 0;
4181 
4182   PetscFunctionBegin;
4183   PetscCall(DMClone(dms[0], superdm));
4184   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4185   (*superdm)->useNatural = PETSC_FALSE;
4186   for (i = 0; i < len; i++) {
4187     if (dms[i]->useNatural && dms[i]->sfMigration) {
4188       PetscSF sfNatural;
4189 
4190       (*superdm)->sfMigration = dms[i]->sfMigration;
4191       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4192       (*superdm)->useNatural = PETSC_TRUE;
4193       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4194       (*superdm)->sfNatural = sfNatural;
4195       break;
4196     }
4197   }
4198   PetscFunctionReturn(PETSC_SUCCESS);
4199 }
4200 
4201 /*@
4202   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4203 
4204   Not Collective
4205 
4206   Input Parameter:
4207 . dm - The `DMPLEX`
4208 
4209   Level: beginner
4210 
4211   Note:
4212   This should be called after all calls to `DMPlexSetCone()`
4213 
4214 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4215 @*/
4216 PetscErrorCode DMPlexSymmetrize(DM dm)
4217 {
4218   DM_Plex  *mesh = (DM_Plex *)dm->data;
4219   PetscInt *offsets;
4220   PetscInt  supportSize;
4221   PetscInt  pStart, pEnd, p;
4222 
4223   PetscFunctionBegin;
4224   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4225   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4226   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4227   /* Calculate support sizes */
4228   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4229   for (p = pStart; p < pEnd; ++p) {
4230     PetscInt dof, off, c;
4231 
4232     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4233     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4234     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4235   }
4236   PetscCall(PetscSectionSetUp(mesh->supportSection));
4237   /* Calculate supports */
4238   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4239   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4240   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4241   for (p = pStart; p < pEnd; ++p) {
4242     PetscInt dof, off, c;
4243 
4244     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4245     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4246     for (c = off; c < off + dof; ++c) {
4247       const PetscInt q = mesh->cones[c];
4248       PetscInt       offS;
4249 
4250       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4251 
4252       mesh->supports[offS + offsets[q]] = p;
4253       ++offsets[q];
4254     }
4255   }
4256   PetscCall(PetscFree(offsets));
4257   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4258   PetscFunctionReturn(PETSC_SUCCESS);
4259 }
4260 
4261 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4262 {
4263   IS stratumIS;
4264 
4265   PetscFunctionBegin;
4266   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4267   if (PetscDefined(USE_DEBUG)) {
4268     PetscInt  qStart, qEnd, numLevels, level;
4269     PetscBool overlap = PETSC_FALSE;
4270     PetscCall(DMLabelGetNumValues(label, &numLevels));
4271     for (level = 0; level < numLevels; level++) {
4272       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4273       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4274         overlap = PETSC_TRUE;
4275         break;
4276       }
4277     }
4278     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);
4279   }
4280   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4281   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4282   PetscCall(ISDestroy(&stratumIS));
4283   PetscFunctionReturn(PETSC_SUCCESS);
4284 }
4285 
4286 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4287 {
4288   PetscInt *pMin, *pMax;
4289   PetscInt  pStart, pEnd;
4290   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4291 
4292   PetscFunctionBegin;
4293   {
4294     DMLabel label2;
4295 
4296     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4297     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4298   }
4299   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4300   for (PetscInt p = pStart; p < pEnd; ++p) {
4301     DMPolytopeType ct;
4302 
4303     PetscCall(DMPlexGetCellType(dm, p, &ct));
4304     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4305     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4306   }
4307   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4308   for (PetscInt d = dmin; d <= dmax; ++d) {
4309     pMin[d] = PETSC_MAX_INT;
4310     pMax[d] = PETSC_MIN_INT;
4311   }
4312   for (PetscInt p = pStart; p < pEnd; ++p) {
4313     DMPolytopeType ct;
4314     PetscInt       d;
4315 
4316     PetscCall(DMPlexGetCellType(dm, p, &ct));
4317     d       = DMPolytopeTypeGetDim(ct);
4318     pMin[d] = PetscMin(p, pMin[d]);
4319     pMax[d] = PetscMax(p, pMax[d]);
4320   }
4321   for (PetscInt d = dmin; d <= dmax; ++d) {
4322     if (pMin[d] > pMax[d]) continue;
4323     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4324   }
4325   PetscCall(PetscFree2(pMin, pMax));
4326   PetscFunctionReturn(PETSC_SUCCESS);
4327 }
4328 
4329 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4330 {
4331   PetscInt pStart, pEnd;
4332   PetscInt numRoots = 0, numLeaves = 0;
4333 
4334   PetscFunctionBegin;
4335   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4336   {
4337     /* Initialize roots and count leaves */
4338     PetscInt sMin = PETSC_MAX_INT;
4339     PetscInt sMax = PETSC_MIN_INT;
4340     PetscInt coneSize, supportSize;
4341 
4342     for (PetscInt p = pStart; p < pEnd; ++p) {
4343       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4344       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4345       if (!coneSize && supportSize) {
4346         sMin = PetscMin(p, sMin);
4347         sMax = PetscMax(p, sMax);
4348         ++numRoots;
4349       } else if (!supportSize && coneSize) {
4350         ++numLeaves;
4351       } else if (!supportSize && !coneSize) {
4352         /* Isolated points */
4353         sMin = PetscMin(p, sMin);
4354         sMax = PetscMax(p, sMax);
4355       }
4356     }
4357     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4358   }
4359 
4360   if (numRoots + numLeaves == (pEnd - pStart)) {
4361     PetscInt sMin = PETSC_MAX_INT;
4362     PetscInt sMax = PETSC_MIN_INT;
4363     PetscInt coneSize, supportSize;
4364 
4365     for (PetscInt p = pStart; p < pEnd; ++p) {
4366       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4367       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4368       if (!supportSize && coneSize) {
4369         sMin = PetscMin(p, sMin);
4370         sMax = PetscMax(p, sMax);
4371       }
4372     }
4373     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4374   } else {
4375     PetscInt level = 0;
4376     PetscInt qStart, qEnd;
4377 
4378     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4379     while (qEnd > qStart) {
4380       PetscInt sMin = PETSC_MAX_INT;
4381       PetscInt sMax = PETSC_MIN_INT;
4382 
4383       for (PetscInt q = qStart; q < qEnd; ++q) {
4384         const PetscInt *support;
4385         PetscInt        supportSize;
4386 
4387         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4388         PetscCall(DMPlexGetSupport(dm, q, &support));
4389         for (PetscInt s = 0; s < supportSize; ++s) {
4390           sMin = PetscMin(support[s], sMin);
4391           sMax = PetscMax(support[s], sMax);
4392         }
4393       }
4394       PetscCall(DMLabelGetNumValues(label, &level));
4395       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4396       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4397     }
4398   }
4399   PetscFunctionReturn(PETSC_SUCCESS);
4400 }
4401 
4402 /*@
4403   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4404 
4405   Collective
4406 
4407   Input Parameter:
4408 . dm - The `DMPLEX`
4409 
4410   Level: beginner
4411 
4412   Notes:
4413   The strata group all points of the same grade, and this function calculates the strata. This
4414   grade can be seen as the height (or depth) of the point in the DAG.
4415 
4416   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4417   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4418   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4419   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4420   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4421   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4422   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4423 
4424   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4425   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4426   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
4427   to interpolate only that one (e0), so that
4428 .vb
4429   cone(c0) = {e0, v2}
4430   cone(e0) = {v0, v1}
4431 .ve
4432   If `DMPlexStratify()` is run on this mesh, it will give depths
4433 .vb
4434    depth 0 = {v0, v1, v2}
4435    depth 1 = {e0, c0}
4436 .ve
4437   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4438 
4439   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4440 
4441 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4442 @*/
4443 PetscErrorCode DMPlexStratify(DM dm)
4444 {
4445   DM_Plex  *mesh = (DM_Plex *)dm->data;
4446   DMLabel   label;
4447   PetscBool flg = PETSC_FALSE;
4448 
4449   PetscFunctionBegin;
4450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4451   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4452 
4453   // Create depth label
4454   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4455   PetscCall(DMCreateLabel(dm, "depth"));
4456   PetscCall(DMPlexGetDepthLabel(dm, &label));
4457 
4458   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4459   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4460   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4461 
4462   { /* just in case there is an empty process */
4463     PetscInt numValues, maxValues = 0, v;
4464 
4465     PetscCall(DMLabelGetNumValues(label, &numValues));
4466     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4467     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4468   }
4469   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4470   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4471   PetscFunctionReturn(PETSC_SUCCESS);
4472 }
4473 
4474 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4475 {
4476   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4477   PetscInt       dim, depth, pheight, coneSize;
4478 
4479   PetscFunctionBeginHot;
4480   PetscCall(DMGetDimension(dm, &dim));
4481   PetscCall(DMPlexGetDepth(dm, &depth));
4482   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4483   pheight = depth - pdepth;
4484   if (depth <= 1) {
4485     switch (pdepth) {
4486     case 0:
4487       ct = DM_POLYTOPE_POINT;
4488       break;
4489     case 1:
4490       switch (coneSize) {
4491       case 2:
4492         ct = DM_POLYTOPE_SEGMENT;
4493         break;
4494       case 3:
4495         ct = DM_POLYTOPE_TRIANGLE;
4496         break;
4497       case 4:
4498         switch (dim) {
4499         case 2:
4500           ct = DM_POLYTOPE_QUADRILATERAL;
4501           break;
4502         case 3:
4503           ct = DM_POLYTOPE_TETRAHEDRON;
4504           break;
4505         default:
4506           break;
4507         }
4508         break;
4509       case 5:
4510         ct = DM_POLYTOPE_PYRAMID;
4511         break;
4512       case 6:
4513         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4514         break;
4515       case 8:
4516         ct = DM_POLYTOPE_HEXAHEDRON;
4517         break;
4518       default:
4519         break;
4520       }
4521     }
4522   } else {
4523     if (pdepth == 0) {
4524       ct = DM_POLYTOPE_POINT;
4525     } else if (pheight == 0) {
4526       switch (dim) {
4527       case 1:
4528         switch (coneSize) {
4529         case 2:
4530           ct = DM_POLYTOPE_SEGMENT;
4531           break;
4532         default:
4533           break;
4534         }
4535         break;
4536       case 2:
4537         switch (coneSize) {
4538         case 3:
4539           ct = DM_POLYTOPE_TRIANGLE;
4540           break;
4541         case 4:
4542           ct = DM_POLYTOPE_QUADRILATERAL;
4543           break;
4544         default:
4545           break;
4546         }
4547         break;
4548       case 3:
4549         switch (coneSize) {
4550         case 4:
4551           ct = DM_POLYTOPE_TETRAHEDRON;
4552           break;
4553         case 5: {
4554           const PetscInt *cone;
4555           PetscInt        faceConeSize;
4556 
4557           PetscCall(DMPlexGetCone(dm, p, &cone));
4558           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4559           switch (faceConeSize) {
4560           case 3:
4561             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4562             break;
4563           case 4:
4564             ct = DM_POLYTOPE_PYRAMID;
4565             break;
4566           }
4567         } break;
4568         case 6:
4569           ct = DM_POLYTOPE_HEXAHEDRON;
4570           break;
4571         default:
4572           break;
4573         }
4574         break;
4575       default:
4576         break;
4577       }
4578     } else if (pheight > 0) {
4579       switch (coneSize) {
4580       case 2:
4581         ct = DM_POLYTOPE_SEGMENT;
4582         break;
4583       case 3:
4584         ct = DM_POLYTOPE_TRIANGLE;
4585         break;
4586       case 4:
4587         ct = DM_POLYTOPE_QUADRILATERAL;
4588         break;
4589       default:
4590         break;
4591       }
4592     }
4593   }
4594   *pt = ct;
4595   PetscFunctionReturn(PETSC_SUCCESS);
4596 }
4597 
4598 /*@
4599   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4600 
4601   Collective
4602 
4603   Input Parameter:
4604 . dm - The `DMPLEX`
4605 
4606   Level: developer
4607 
4608   Note:
4609   This function is normally called automatically when a cell type is requested. It creates an
4610   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4611   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4612 
4613   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4614 
4615 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4616 @*/
4617 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4618 {
4619   DM_Plex *mesh;
4620   DMLabel  ctLabel;
4621   PetscInt pStart, pEnd, p;
4622 
4623   PetscFunctionBegin;
4624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4625   mesh = (DM_Plex *)dm->data;
4626   PetscCall(DMCreateLabel(dm, "celltype"));
4627   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4628   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4629   PetscCall(PetscFree(mesh->cellTypes));
4630   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4631   for (p = pStart; p < pEnd; ++p) {
4632     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4633     PetscInt       pdepth;
4634 
4635     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4636     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4637     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4638     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4639     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4640   }
4641   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4642   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4643   PetscFunctionReturn(PETSC_SUCCESS);
4644 }
4645 
4646 /*@C
4647   DMPlexGetJoin - Get an array for the join of the set of points
4648 
4649   Not Collective
4650 
4651   Input Parameters:
4652 + dm        - The `DMPLEX` object
4653 . numPoints - The number of input points for the join
4654 - points    - The input points
4655 
4656   Output Parameters:
4657 + numCoveredPoints - The number of points in the join
4658 - coveredPoints    - The points in the join
4659 
4660   Level: intermediate
4661 
4662   Note:
4663   Currently, this is restricted to a single level join
4664 
4665   Fortran Notes:
4666   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4667 
4668 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4669 @*/
4670 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4671 {
4672   DM_Plex  *mesh = (DM_Plex *)dm->data;
4673   PetscInt *join[2];
4674   PetscInt  joinSize, i = 0;
4675   PetscInt  dof, off, p, c, m;
4676   PetscInt  maxSupportSize;
4677 
4678   PetscFunctionBegin;
4679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4680   PetscAssertPointer(points, 3);
4681   PetscAssertPointer(numCoveredPoints, 4);
4682   PetscAssertPointer(coveredPoints, 5);
4683   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4684   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4685   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4686   /* Copy in support of first point */
4687   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4688   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4689   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4690   /* Check each successive support */
4691   for (p = 1; p < numPoints; ++p) {
4692     PetscInt newJoinSize = 0;
4693 
4694     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4695     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4696     for (c = 0; c < dof; ++c) {
4697       const PetscInt point = mesh->supports[off + c];
4698 
4699       for (m = 0; m < joinSize; ++m) {
4700         if (point == join[i][m]) {
4701           join[1 - i][newJoinSize++] = point;
4702           break;
4703         }
4704       }
4705     }
4706     joinSize = newJoinSize;
4707     i        = 1 - i;
4708   }
4709   *numCoveredPoints = joinSize;
4710   *coveredPoints    = join[i];
4711   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4712   PetscFunctionReturn(PETSC_SUCCESS);
4713 }
4714 
4715 /*@C
4716   DMPlexRestoreJoin - Restore an array for the join of the set of points
4717 
4718   Not Collective
4719 
4720   Input Parameters:
4721 + dm        - The `DMPLEX` object
4722 . numPoints - The number of input points for the join
4723 - points    - The input points
4724 
4725   Output Parameters:
4726 + numCoveredPoints - The number of points in the join
4727 - coveredPoints    - The points in the join
4728 
4729   Level: intermediate
4730 
4731   Fortran Notes:
4732   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4733 
4734 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4735 @*/
4736 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4737 {
4738   PetscFunctionBegin;
4739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4740   if (points) PetscAssertPointer(points, 3);
4741   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4742   PetscAssertPointer(coveredPoints, 5);
4743   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4744   if (numCoveredPoints) *numCoveredPoints = 0;
4745   PetscFunctionReturn(PETSC_SUCCESS);
4746 }
4747 
4748 /*@C
4749   DMPlexGetFullJoin - Get an array for the join of the set of points
4750 
4751   Not Collective
4752 
4753   Input Parameters:
4754 + dm        - The `DMPLEX` object
4755 . numPoints - The number of input points for the join
4756 - points    - The input points
4757 
4758   Output Parameters:
4759 + numCoveredPoints - The number of points in the join
4760 - coveredPoints    - The points in the join
4761 
4762   Level: intermediate
4763 
4764   Fortran Notes:
4765   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4766 
4767 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4768 @*/
4769 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4770 {
4771   PetscInt *offsets, **closures;
4772   PetscInt *join[2];
4773   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4774   PetscInt  p, d, c, m, ms;
4775 
4776   PetscFunctionBegin;
4777   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4778   PetscAssertPointer(points, 3);
4779   PetscAssertPointer(numCoveredPoints, 4);
4780   PetscAssertPointer(coveredPoints, 5);
4781 
4782   PetscCall(DMPlexGetDepth(dm, &depth));
4783   PetscCall(PetscCalloc1(numPoints, &closures));
4784   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4785   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4786   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4787   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4788   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4789 
4790   for (p = 0; p < numPoints; ++p) {
4791     PetscInt closureSize;
4792 
4793     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4794 
4795     offsets[p * (depth + 2) + 0] = 0;
4796     for (d = 0; d < depth + 1; ++d) {
4797       PetscInt pStart, pEnd, i;
4798 
4799       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4800       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4801         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4802           offsets[p * (depth + 2) + d + 1] = i;
4803           break;
4804         }
4805       }
4806       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4807     }
4808     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);
4809   }
4810   for (d = 0; d < depth + 1; ++d) {
4811     PetscInt dof;
4812 
4813     /* Copy in support of first point */
4814     dof = offsets[d + 1] - offsets[d];
4815     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4816     /* Check each successive cone */
4817     for (p = 1; p < numPoints && joinSize; ++p) {
4818       PetscInt newJoinSize = 0;
4819 
4820       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4821       for (c = 0; c < dof; ++c) {
4822         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4823 
4824         for (m = 0; m < joinSize; ++m) {
4825           if (point == join[i][m]) {
4826             join[1 - i][newJoinSize++] = point;
4827             break;
4828           }
4829         }
4830       }
4831       joinSize = newJoinSize;
4832       i        = 1 - i;
4833     }
4834     if (joinSize) break;
4835   }
4836   *numCoveredPoints = joinSize;
4837   *coveredPoints    = join[i];
4838   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4839   PetscCall(PetscFree(closures));
4840   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4841   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4842   PetscFunctionReturn(PETSC_SUCCESS);
4843 }
4844 
4845 /*@C
4846   DMPlexGetMeet - Get an array for the meet of the set of points
4847 
4848   Not Collective
4849 
4850   Input Parameters:
4851 + dm        - The `DMPLEX` object
4852 . numPoints - The number of input points for the meet
4853 - points    - The input points
4854 
4855   Output Parameters:
4856 + numCoveringPoints - The number of points in the meet
4857 - coveringPoints    - The points in the meet
4858 
4859   Level: intermediate
4860 
4861   Note:
4862   Currently, this is restricted to a single level meet
4863 
4864   Fortran Notes:
4865   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4866 
4867 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4868 @*/
4869 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4870 {
4871   DM_Plex  *mesh = (DM_Plex *)dm->data;
4872   PetscInt *meet[2];
4873   PetscInt  meetSize, i = 0;
4874   PetscInt  dof, off, p, c, m;
4875   PetscInt  maxConeSize;
4876 
4877   PetscFunctionBegin;
4878   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4879   PetscAssertPointer(points, 3);
4880   PetscAssertPointer(numCoveringPoints, 4);
4881   PetscAssertPointer(coveringPoints, 5);
4882   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4883   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4884   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4885   /* Copy in cone of first point */
4886   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4887   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4888   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4889   /* Check each successive cone */
4890   for (p = 1; p < numPoints; ++p) {
4891     PetscInt newMeetSize = 0;
4892 
4893     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4894     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4895     for (c = 0; c < dof; ++c) {
4896       const PetscInt point = mesh->cones[off + c];
4897 
4898       for (m = 0; m < meetSize; ++m) {
4899         if (point == meet[i][m]) {
4900           meet[1 - i][newMeetSize++] = point;
4901           break;
4902         }
4903       }
4904     }
4905     meetSize = newMeetSize;
4906     i        = 1 - i;
4907   }
4908   *numCoveringPoints = meetSize;
4909   *coveringPoints    = meet[i];
4910   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4911   PetscFunctionReturn(PETSC_SUCCESS);
4912 }
4913 
4914 /*@C
4915   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4916 
4917   Not Collective
4918 
4919   Input Parameters:
4920 + dm        - The `DMPLEX` object
4921 . numPoints - The number of input points for the meet
4922 - points    - The input points
4923 
4924   Output Parameters:
4925 + numCoveredPoints - The number of points in the meet
4926 - coveredPoints    - The points in the meet
4927 
4928   Level: intermediate
4929 
4930   Fortran Notes:
4931   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4932 
4933 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4934 @*/
4935 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4936 {
4937   PetscFunctionBegin;
4938   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4939   if (points) PetscAssertPointer(points, 3);
4940   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4941   PetscAssertPointer(coveredPoints, 5);
4942   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4943   if (numCoveredPoints) *numCoveredPoints = 0;
4944   PetscFunctionReturn(PETSC_SUCCESS);
4945 }
4946 
4947 /*@C
4948   DMPlexGetFullMeet - Get an array for the meet of the set of points
4949 
4950   Not Collective
4951 
4952   Input Parameters:
4953 + dm        - The `DMPLEX` object
4954 . numPoints - The number of input points for the meet
4955 - points    - The input points
4956 
4957   Output Parameters:
4958 + numCoveredPoints - The number of points in the meet
4959 - coveredPoints    - The points in the meet
4960 
4961   Level: intermediate
4962 
4963   Fortran Notes:
4964   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4965 
4966 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4967 @*/
4968 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4969 {
4970   PetscInt *offsets, **closures;
4971   PetscInt *meet[2];
4972   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4973   PetscInt  p, h, c, m, mc;
4974 
4975   PetscFunctionBegin;
4976   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4977   PetscAssertPointer(points, 3);
4978   PetscAssertPointer(numCoveredPoints, 4);
4979   PetscAssertPointer(coveredPoints, 5);
4980 
4981   PetscCall(DMPlexGetDepth(dm, &height));
4982   PetscCall(PetscMalloc1(numPoints, &closures));
4983   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4984   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4985   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4986   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4987   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4988 
4989   for (p = 0; p < numPoints; ++p) {
4990     PetscInt closureSize;
4991 
4992     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4993 
4994     offsets[p * (height + 2) + 0] = 0;
4995     for (h = 0; h < height + 1; ++h) {
4996       PetscInt pStart, pEnd, i;
4997 
4998       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4999       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5000         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5001           offsets[p * (height + 2) + h + 1] = i;
5002           break;
5003         }
5004       }
5005       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5006     }
5007     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);
5008   }
5009   for (h = 0; h < height + 1; ++h) {
5010     PetscInt dof;
5011 
5012     /* Copy in cone of first point */
5013     dof = offsets[h + 1] - offsets[h];
5014     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5015     /* Check each successive cone */
5016     for (p = 1; p < numPoints && meetSize; ++p) {
5017       PetscInt newMeetSize = 0;
5018 
5019       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5020       for (c = 0; c < dof; ++c) {
5021         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5022 
5023         for (m = 0; m < meetSize; ++m) {
5024           if (point == meet[i][m]) {
5025             meet[1 - i][newMeetSize++] = point;
5026             break;
5027           }
5028         }
5029       }
5030       meetSize = newMeetSize;
5031       i        = 1 - i;
5032     }
5033     if (meetSize) break;
5034   }
5035   *numCoveredPoints = meetSize;
5036   *coveredPoints    = meet[i];
5037   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5038   PetscCall(PetscFree(closures));
5039   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5040   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5041   PetscFunctionReturn(PETSC_SUCCESS);
5042 }
5043 
5044 /*@C
5045   DMPlexEqual - Determine if two `DM` have the same topology
5046 
5047   Not Collective
5048 
5049   Input Parameters:
5050 + dmA - A `DMPLEX` object
5051 - dmB - A `DMPLEX` object
5052 
5053   Output Parameter:
5054 . equal - `PETSC_TRUE` if the topologies are identical
5055 
5056   Level: intermediate
5057 
5058   Note:
5059   We are not solving graph isomorphism, so we do not permute.
5060 
5061 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5062 @*/
5063 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5064 {
5065   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5066 
5067   PetscFunctionBegin;
5068   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5069   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5070   PetscAssertPointer(equal, 3);
5071 
5072   *equal = PETSC_FALSE;
5073   PetscCall(DMPlexGetDepth(dmA, &depth));
5074   PetscCall(DMPlexGetDepth(dmB, &depthB));
5075   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5076   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5077   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5078   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5079   for (p = pStart; p < pEnd; ++p) {
5080     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5081     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5082 
5083     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5084     PetscCall(DMPlexGetCone(dmA, p, &cone));
5085     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5086     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5087     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5088     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5089     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5090     for (c = 0; c < coneSize; ++c) {
5091       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5092       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5093     }
5094     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5095     PetscCall(DMPlexGetSupport(dmA, p, &support));
5096     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5097     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5098     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5099     for (s = 0; s < supportSize; ++s) {
5100       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5101     }
5102   }
5103   *equal = PETSC_TRUE;
5104   PetscFunctionReturn(PETSC_SUCCESS);
5105 }
5106 
5107 /*@C
5108   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5109 
5110   Not Collective
5111 
5112   Input Parameters:
5113 + dm         - The `DMPLEX`
5114 . cellDim    - The cell dimension
5115 - numCorners - The number of vertices on a cell
5116 
5117   Output Parameter:
5118 . numFaceVertices - The number of vertices on a face
5119 
5120   Level: developer
5121 
5122   Note:
5123   Of course this can only work for a restricted set of symmetric shapes
5124 
5125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5126 @*/
5127 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5128 {
5129   MPI_Comm comm;
5130 
5131   PetscFunctionBegin;
5132   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5133   PetscAssertPointer(numFaceVertices, 4);
5134   switch (cellDim) {
5135   case 0:
5136     *numFaceVertices = 0;
5137     break;
5138   case 1:
5139     *numFaceVertices = 1;
5140     break;
5141   case 2:
5142     switch (numCorners) {
5143     case 3:                 /* triangle */
5144       *numFaceVertices = 2; /* Edge has 2 vertices */
5145       break;
5146     case 4:                 /* quadrilateral */
5147       *numFaceVertices = 2; /* Edge has 2 vertices */
5148       break;
5149     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5150       *numFaceVertices = 3; /* Edge has 3 vertices */
5151       break;
5152     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5153       *numFaceVertices = 3; /* Edge has 3 vertices */
5154       break;
5155     default:
5156       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5157     }
5158     break;
5159   case 3:
5160     switch (numCorners) {
5161     case 4:                 /* tetradehdron */
5162       *numFaceVertices = 3; /* Face has 3 vertices */
5163       break;
5164     case 6:                 /* tet cohesive cells */
5165       *numFaceVertices = 4; /* Face has 4 vertices */
5166       break;
5167     case 8:                 /* hexahedron */
5168       *numFaceVertices = 4; /* Face has 4 vertices */
5169       break;
5170     case 9:                 /* tet cohesive Lagrange cells */
5171       *numFaceVertices = 6; /* Face has 6 vertices */
5172       break;
5173     case 10:                /* quadratic tetrahedron */
5174       *numFaceVertices = 6; /* Face has 6 vertices */
5175       break;
5176     case 12:                /* hex cohesive Lagrange cells */
5177       *numFaceVertices = 6; /* Face has 6 vertices */
5178       break;
5179     case 18:                /* quadratic tet cohesive Lagrange cells */
5180       *numFaceVertices = 6; /* Face has 6 vertices */
5181       break;
5182     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5183       *numFaceVertices = 9; /* Face has 9 vertices */
5184       break;
5185     default:
5186       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5187     }
5188     break;
5189   default:
5190     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5191   }
5192   PetscFunctionReturn(PETSC_SUCCESS);
5193 }
5194 
5195 /*@
5196   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5197 
5198   Not Collective
5199 
5200   Input Parameter:
5201 . dm - The `DMPLEX` object
5202 
5203   Output Parameter:
5204 . depthLabel - The `DMLabel` recording point depth
5205 
5206   Level: developer
5207 
5208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5209 @*/
5210 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5211 {
5212   PetscFunctionBegin;
5213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5214   PetscAssertPointer(depthLabel, 2);
5215   *depthLabel = dm->depthLabel;
5216   PetscFunctionReturn(PETSC_SUCCESS);
5217 }
5218 
5219 /*@
5220   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5221 
5222   Not Collective
5223 
5224   Input Parameter:
5225 . dm - The `DMPLEX` object
5226 
5227   Output Parameter:
5228 . depth - The number of strata (breadth first levels) in the DAG
5229 
5230   Level: developer
5231 
5232   Notes:
5233   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5234 
5235   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5236 
5237   An empty mesh gives -1.
5238 
5239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5240 @*/
5241 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5242 {
5243   DM_Plex *mesh = (DM_Plex *)dm->data;
5244   DMLabel  label;
5245   PetscInt d = 0;
5246 
5247   PetscFunctionBegin;
5248   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5249   PetscAssertPointer(depth, 2);
5250   if (mesh->tr) {
5251     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5252   } else {
5253     PetscCall(DMPlexGetDepthLabel(dm, &label));
5254     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5255     *depth = d - 1;
5256   }
5257   PetscFunctionReturn(PETSC_SUCCESS);
5258 }
5259 
5260 /*@
5261   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5262 
5263   Not Collective
5264 
5265   Input Parameters:
5266 + dm    - The `DMPLEX` object
5267 - depth - The requested depth
5268 
5269   Output Parameters:
5270 + start - The first point at this `depth`
5271 - end   - One beyond the last point at this `depth`
5272 
5273   Level: developer
5274 
5275   Notes:
5276   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5277   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5278   higher dimension, e.g., "edges".
5279 
5280 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5281 @*/
5282 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5283 {
5284   DM_Plex *mesh = (DM_Plex *)dm->data;
5285   DMLabel  label;
5286   PetscInt pStart, pEnd;
5287 
5288   PetscFunctionBegin;
5289   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5290   if (start) {
5291     PetscAssertPointer(start, 3);
5292     *start = 0;
5293   }
5294   if (end) {
5295     PetscAssertPointer(end, 4);
5296     *end = 0;
5297   }
5298   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5299   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5300   if (depth < 0) {
5301     if (start) *start = pStart;
5302     if (end) *end = pEnd;
5303     PetscFunctionReturn(PETSC_SUCCESS);
5304   }
5305   if (mesh->tr) {
5306     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5307   } else {
5308     PetscCall(DMPlexGetDepthLabel(dm, &label));
5309     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5310     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5311   }
5312   PetscFunctionReturn(PETSC_SUCCESS);
5313 }
5314 
5315 /*@
5316   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5317 
5318   Not Collective
5319 
5320   Input Parameters:
5321 + dm     - The `DMPLEX` object
5322 - height - The requested height
5323 
5324   Output Parameters:
5325 + start - The first point at this `height`
5326 - end   - One beyond the last point at this `height`
5327 
5328   Level: developer
5329 
5330   Notes:
5331   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5332   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5333   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5334 
5335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5336 @*/
5337 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5338 {
5339   DMLabel  label;
5340   PetscInt depth, pStart, pEnd;
5341 
5342   PetscFunctionBegin;
5343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5344   if (start) {
5345     PetscAssertPointer(start, 3);
5346     *start = 0;
5347   }
5348   if (end) {
5349     PetscAssertPointer(end, 4);
5350     *end = 0;
5351   }
5352   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5353   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5354   if (height < 0) {
5355     if (start) *start = pStart;
5356     if (end) *end = pEnd;
5357     PetscFunctionReturn(PETSC_SUCCESS);
5358   }
5359   PetscCall(DMPlexGetDepthLabel(dm, &label));
5360   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5361   else PetscCall(DMGetDimension(dm, &depth));
5362   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5363   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5364   PetscFunctionReturn(PETSC_SUCCESS);
5365 }
5366 
5367 /*@
5368   DMPlexGetPointDepth - Get the `depth` of a given point
5369 
5370   Not Collective
5371 
5372   Input Parameters:
5373 + dm    - The `DMPLEX` object
5374 - point - The point
5375 
5376   Output Parameter:
5377 . depth - The depth of the `point`
5378 
5379   Level: intermediate
5380 
5381 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5382 @*/
5383 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5384 {
5385   PetscFunctionBegin;
5386   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5387   PetscAssertPointer(depth, 3);
5388   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5389   PetscFunctionReturn(PETSC_SUCCESS);
5390 }
5391 
5392 /*@
5393   DMPlexGetPointHeight - Get the `height` of a given point
5394 
5395   Not Collective
5396 
5397   Input Parameters:
5398 + dm    - The `DMPLEX` object
5399 - point - The point
5400 
5401   Output Parameter:
5402 . height - The height of the `point`
5403 
5404   Level: intermediate
5405 
5406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5407 @*/
5408 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5409 {
5410   PetscInt n, pDepth;
5411 
5412   PetscFunctionBegin;
5413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5414   PetscAssertPointer(height, 3);
5415   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5416   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5417   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5418   PetscFunctionReturn(PETSC_SUCCESS);
5419 }
5420 
5421 /*@
5422   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5423 
5424   Not Collective
5425 
5426   Input Parameter:
5427 . dm - The `DMPLEX` object
5428 
5429   Output Parameter:
5430 . celltypeLabel - The `DMLabel` recording cell polytope type
5431 
5432   Level: developer
5433 
5434   Note:
5435   This function will trigger automatica computation of cell types. This can be disabled by calling
5436   `DMCreateLabel`(dm, "celltype") beforehand.
5437 
5438 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5439 @*/
5440 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5441 {
5442   PetscFunctionBegin;
5443   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5444   PetscAssertPointer(celltypeLabel, 2);
5445   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5446   *celltypeLabel = dm->celltypeLabel;
5447   PetscFunctionReturn(PETSC_SUCCESS);
5448 }
5449 
5450 /*@
5451   DMPlexGetCellType - Get the polytope type of a given cell
5452 
5453   Not Collective
5454 
5455   Input Parameters:
5456 + dm   - The `DMPLEX` object
5457 - cell - The cell
5458 
5459   Output Parameter:
5460 . celltype - The polytope type of the cell
5461 
5462   Level: intermediate
5463 
5464 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5465 @*/
5466 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5467 {
5468   DM_Plex *mesh = (DM_Plex *)dm->data;
5469   DMLabel  label;
5470   PetscInt ct;
5471 
5472   PetscFunctionBegin;
5473   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5474   PetscAssertPointer(celltype, 3);
5475   if (mesh->tr) {
5476     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5477   } else {
5478     PetscInt pStart, pEnd;
5479 
5480     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5481     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5482       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5483       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5484       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5485       for (PetscInt p = pStart; p < pEnd; p++) {
5486         PetscCall(DMLabelGetValue(label, p, &ct));
5487         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5488       }
5489     }
5490     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5491     if (PetscDefined(USE_DEBUG)) {
5492       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5493       PetscCall(DMLabelGetValue(label, cell, &ct));
5494       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5495       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5496     }
5497   }
5498   PetscFunctionReturn(PETSC_SUCCESS);
5499 }
5500 
5501 /*@
5502   DMPlexSetCellType - Set the polytope type of a given cell
5503 
5504   Not Collective
5505 
5506   Input Parameters:
5507 + dm       - The `DMPLEX` object
5508 . cell     - The cell
5509 - celltype - The polytope type of the cell
5510 
5511   Level: advanced
5512 
5513   Note:
5514   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5515   is executed. This function will override the computed type. However, if automatic classification will not succeed
5516   and a user wants to manually specify all types, the classification must be disabled by calling
5517   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5518 
5519 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5520 @*/
5521 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5522 {
5523   DM_Plex *mesh = (DM_Plex *)dm->data;
5524   DMLabel  label;
5525   PetscInt pStart, pEnd;
5526 
5527   PetscFunctionBegin;
5528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5529   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5530   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5531   PetscCall(DMLabelSetValue(label, cell, celltype));
5532   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5533   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5534   PetscFunctionReturn(PETSC_SUCCESS);
5535 }
5536 
5537 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5538 {
5539   PetscSection section;
5540   PetscInt     maxHeight;
5541   const char  *prefix;
5542 
5543   PetscFunctionBegin;
5544   PetscCall(DMClone(dm, cdm));
5545   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5546   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5547   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5548   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5549   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5550   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5551   PetscCall(DMSetLocalSection(*cdm, section));
5552   PetscCall(PetscSectionDestroy(&section));
5553 
5554   PetscCall(DMSetNumFields(*cdm, 1));
5555   PetscCall(DMCreateDS(*cdm));
5556   (*cdm)->cloneOpts = PETSC_TRUE;
5557   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5558   PetscFunctionReturn(PETSC_SUCCESS);
5559 }
5560 
5561 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5562 {
5563   Vec coordsLocal, cellCoordsLocal;
5564   DM  coordsDM, cellCoordsDM;
5565 
5566   PetscFunctionBegin;
5567   *field = NULL;
5568   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5569   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5570   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5571   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5572   if (coordsLocal && coordsDM) {
5573     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5574     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5575   }
5576   PetscFunctionReturn(PETSC_SUCCESS);
5577 }
5578 
5579 /*@C
5580   DMPlexGetConeSection - Return a section which describes the layout of cone data
5581 
5582   Not Collective
5583 
5584   Input Parameter:
5585 . dm - The `DMPLEX` object
5586 
5587   Output Parameter:
5588 . section - The `PetscSection` object
5589 
5590   Level: developer
5591 
5592 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5593 @*/
5594 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5595 {
5596   DM_Plex *mesh = (DM_Plex *)dm->data;
5597 
5598   PetscFunctionBegin;
5599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5600   if (section) *section = mesh->coneSection;
5601   PetscFunctionReturn(PETSC_SUCCESS);
5602 }
5603 
5604 /*@C
5605   DMPlexGetSupportSection - Return a section which describes the layout of support data
5606 
5607   Not Collective
5608 
5609   Input Parameter:
5610 . dm - The `DMPLEX` object
5611 
5612   Output Parameter:
5613 . section - The `PetscSection` object
5614 
5615   Level: developer
5616 
5617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5618 @*/
5619 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5620 {
5621   DM_Plex *mesh = (DM_Plex *)dm->data;
5622 
5623   PetscFunctionBegin;
5624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5625   if (section) *section = mesh->supportSection;
5626   PetscFunctionReturn(PETSC_SUCCESS);
5627 }
5628 
5629 /*@C
5630   DMPlexGetCones - Return cone data
5631 
5632   Not Collective
5633 
5634   Input Parameter:
5635 . dm - The `DMPLEX` object
5636 
5637   Output Parameter:
5638 . cones - The cone for each point
5639 
5640   Level: developer
5641 
5642 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5643 @*/
5644 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5645 {
5646   DM_Plex *mesh = (DM_Plex *)dm->data;
5647 
5648   PetscFunctionBegin;
5649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5650   if (cones) *cones = mesh->cones;
5651   PetscFunctionReturn(PETSC_SUCCESS);
5652 }
5653 
5654 /*@C
5655   DMPlexGetConeOrientations - Return cone orientation data
5656 
5657   Not Collective
5658 
5659   Input Parameter:
5660 . dm - The `DMPLEX` object
5661 
5662   Output Parameter:
5663 . coneOrientations - The array of cone orientations for all points
5664 
5665   Level: developer
5666 
5667   Notes:
5668   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5669 
5670   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5671 
5672 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5673 @*/
5674 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5675 {
5676   DM_Plex *mesh = (DM_Plex *)dm->data;
5677 
5678   PetscFunctionBegin;
5679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5680   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5681   PetscFunctionReturn(PETSC_SUCCESS);
5682 }
5683 
5684 /******************************** FEM Support **********************************/
5685 
5686 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5687 {
5688   PetscInt depth;
5689 
5690   PetscFunctionBegin;
5691   PetscCall(DMPlexGetDepth(plex, &depth));
5692   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5693   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5694   PetscFunctionReturn(PETSC_SUCCESS);
5695 }
5696 
5697 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5698 {
5699   PetscInt depth;
5700 
5701   PetscFunctionBegin;
5702   PetscCall(DMPlexGetDepth(plex, &depth));
5703   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5704   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5705   PetscFunctionReturn(PETSC_SUCCESS);
5706 }
5707 
5708 /*
5709  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5710  representing a line in the section.
5711 */
5712 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5713 {
5714   PetscObject  obj;
5715   PetscClassId id;
5716   PetscFE      fe = NULL;
5717 
5718   PetscFunctionBeginHot;
5719   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5720   PetscCall(DMGetField(dm, field, NULL, &obj));
5721   PetscCall(PetscObjectGetClassId(obj, &id));
5722   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5723 
5724   if (!fe) {
5725     /* Assume the full interpolated mesh is in the chart; lines in particular */
5726     /* An order k SEM disc has k-1 dofs on an edge */
5727     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5728     *k = *k / *Nc + 1;
5729   } else {
5730     PetscInt       dual_space_size, dim;
5731     PetscDualSpace dsp;
5732 
5733     PetscCall(DMGetDimension(dm, &dim));
5734     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5735     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5736     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5737     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5738     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5739   }
5740   PetscFunctionReturn(PETSC_SUCCESS);
5741 }
5742 
5743 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5744 {
5745   PetscFunctionBeginHot;
5746   if (tensor) {
5747     *dof = PetscPowInt(k + 1, dim);
5748   } else {
5749     switch (dim) {
5750     case 1:
5751       *dof = k + 1;
5752       break;
5753     case 2:
5754       *dof = ((k + 1) * (k + 2)) / 2;
5755       break;
5756     case 3:
5757       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5758       break;
5759     default:
5760       *dof = 0;
5761     }
5762   }
5763   PetscFunctionReturn(PETSC_SUCCESS);
5764 }
5765 
5766 /*@
5767 
5768   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5769   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5770   section provided (or the section of the `DM`).
5771 
5772   Input Parameters:
5773 + dm      - The `DM`
5774 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5775 - section - The `PetscSection` to reorder, or `NULL` for the default section
5776 
5777   Example:
5778   A typical interpolated single-quad mesh might order points as
5779 .vb
5780   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5781 
5782   v4 -- e6 -- v3
5783   |           |
5784   e7    c0    e8
5785   |           |
5786   v1 -- e5 -- v2
5787 .ve
5788 
5789   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5790   dofs in the order of points, e.g.,
5791 .vb
5792     c0 -> [0,1,2,3]
5793     v1 -> [4]
5794     ...
5795     e5 -> [8, 9]
5796 .ve
5797 
5798   which corresponds to the dofs
5799 .vb
5800     6   10  11  7
5801     13  2   3   15
5802     12  0   1   14
5803     4   8   9   5
5804 .ve
5805 
5806   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5807 .vb
5808   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5809 .ve
5810 
5811   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5812 .vb
5813    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5814 .ve
5815 
5816   Level: developer
5817 
5818   Notes:
5819   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5820   degree of the basis.
5821 
5822   This is required to run with libCEED.
5823 
5824 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5825 @*/
5826 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5827 {
5828   DMLabel   label;
5829   PetscInt  dim, depth = -1, eStart = -1, Nf;
5830   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5831 
5832   PetscFunctionBegin;
5833   PetscCall(DMGetDimension(dm, &dim));
5834   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5835   if (point < 0) {
5836     PetscInt sStart, sEnd;
5837 
5838     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5839     point = sEnd - sStart ? sStart : point;
5840   }
5841   PetscCall(DMPlexGetDepthLabel(dm, &label));
5842   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5843   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5844   if (depth == 1) {
5845     eStart = point;
5846   } else if (depth == dim) {
5847     const PetscInt *cone;
5848 
5849     PetscCall(DMPlexGetCone(dm, point, &cone));
5850     if (dim == 2) eStart = cone[0];
5851     else if (dim == 3) {
5852       const PetscInt *cone2;
5853       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5854       eStart = cone2[0];
5855     } 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);
5856   } 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);
5857 
5858   PetscCall(PetscSectionGetNumFields(section, &Nf));
5859   for (PetscInt d = 1; d <= dim; d++) {
5860     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5861     PetscInt *perm;
5862 
5863     for (f = 0; f < Nf; ++f) {
5864       PetscInt dof;
5865 
5866       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5867       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5868       if (!continuous && d < dim) continue;
5869       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5870       size += dof * Nc;
5871     }
5872     PetscCall(PetscMalloc1(size, &perm));
5873     for (f = 0; f < Nf; ++f) {
5874       switch (d) {
5875       case 1:
5876         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5877         if (!continuous && d < dim) continue;
5878         /*
5879          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5880          We want              [ vtx0; edge of length k-1; vtx1 ]
5881          */
5882         if (continuous) {
5883           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5884           for (i = 0; i < k - 1; i++)
5885             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5886           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5887           foffset = offset;
5888         } else {
5889           PetscInt dof;
5890 
5891           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5892           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5893           foffset = offset;
5894         }
5895         break;
5896       case 2:
5897         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5898         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5899         if (!continuous && d < dim) continue;
5900         /* The SEM order is
5901 
5902          v_lb, {e_b}, v_rb,
5903          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5904          v_lt, reverse {e_t}, v_rt
5905          */
5906         if (continuous) {
5907           const PetscInt of   = 0;
5908           const PetscInt oeb  = of + PetscSqr(k - 1);
5909           const PetscInt oer  = oeb + (k - 1);
5910           const PetscInt oet  = oer + (k - 1);
5911           const PetscInt oel  = oet + (k - 1);
5912           const PetscInt ovlb = oel + (k - 1);
5913           const PetscInt ovrb = ovlb + 1;
5914           const PetscInt ovrt = ovrb + 1;
5915           const PetscInt ovlt = ovrt + 1;
5916           PetscInt       o;
5917 
5918           /* bottom */
5919           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5920           for (o = oeb; o < oer; ++o)
5921             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5922           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5923           /* middle */
5924           for (i = 0; i < k - 1; ++i) {
5925             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5926             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5927               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5928             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5929           }
5930           /* top */
5931           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5932           for (o = oel - 1; o >= oet; --o)
5933             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5934           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5935           foffset = offset;
5936         } else {
5937           PetscInt dof;
5938 
5939           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5940           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5941           foffset = offset;
5942         }
5943         break;
5944       case 3:
5945         /* The original hex closure is
5946 
5947          {c,
5948          f_b, f_t, f_f, f_b, f_r, f_l,
5949          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5950          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5951          */
5952         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5953         if (!continuous && d < dim) continue;
5954         /* The SEM order is
5955          Bottom Slice
5956          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5957          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5958          v_blb, {e_bb}, v_brb,
5959 
5960          Middle Slice (j)
5961          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5962          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5963          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5964 
5965          Top Slice
5966          v_tlf, {e_tf}, v_trf,
5967          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5968          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5969          */
5970         if (continuous) {
5971           const PetscInt oc    = 0;
5972           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5973           const PetscInt oft   = ofb + PetscSqr(k - 1);
5974           const PetscInt off   = oft + PetscSqr(k - 1);
5975           const PetscInt ofk   = off + PetscSqr(k - 1);
5976           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5977           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5978           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5979           const PetscInt oebb  = oebl + (k - 1);
5980           const PetscInt oebr  = oebb + (k - 1);
5981           const PetscInt oebf  = oebr + (k - 1);
5982           const PetscInt oetf  = oebf + (k - 1);
5983           const PetscInt oetr  = oetf + (k - 1);
5984           const PetscInt oetb  = oetr + (k - 1);
5985           const PetscInt oetl  = oetb + (k - 1);
5986           const PetscInt oerf  = oetl + (k - 1);
5987           const PetscInt oelf  = oerf + (k - 1);
5988           const PetscInt oelb  = oelf + (k - 1);
5989           const PetscInt oerb  = oelb + (k - 1);
5990           const PetscInt ovblf = oerb + (k - 1);
5991           const PetscInt ovblb = ovblf + 1;
5992           const PetscInt ovbrb = ovblb + 1;
5993           const PetscInt ovbrf = ovbrb + 1;
5994           const PetscInt ovtlf = ovbrf + 1;
5995           const PetscInt ovtrf = ovtlf + 1;
5996           const PetscInt ovtrb = ovtrf + 1;
5997           const PetscInt ovtlb = ovtrb + 1;
5998           PetscInt       o, n;
5999 
6000           /* Bottom Slice */
6001           /*   bottom */
6002           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6003           for (o = oetf - 1; o >= oebf; --o)
6004             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6005           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6006           /*   middle */
6007           for (i = 0; i < k - 1; ++i) {
6008             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6009             for (n = 0; n < k - 1; ++n) {
6010               o = ofb + n * (k - 1) + i;
6011               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6012             }
6013             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6014           }
6015           /*   top */
6016           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6017           for (o = oebb; o < oebr; ++o)
6018             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6019           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6020 
6021           /* Middle Slice */
6022           for (j = 0; j < k - 1; ++j) {
6023             /*   bottom */
6024             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6025             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6026               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6027             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6028             /*   middle */
6029             for (i = 0; i < k - 1; ++i) {
6030               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6031               for (n = 0; n < k - 1; ++n)
6032                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6033               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6034             }
6035             /*   top */
6036             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6037             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6038               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6039             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6040           }
6041 
6042           /* Top Slice */
6043           /*   bottom */
6044           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6045           for (o = oetf; o < oetr; ++o)
6046             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6047           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6048           /*   middle */
6049           for (i = 0; i < k - 1; ++i) {
6050             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6051             for (n = 0; n < k - 1; ++n)
6052               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6053             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6054           }
6055           /*   top */
6056           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6057           for (o = oetl - 1; o >= oetb; --o)
6058             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6059           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6060 
6061           foffset = offset;
6062         } else {
6063           PetscInt dof;
6064 
6065           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6066           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6067           foffset = offset;
6068         }
6069         break;
6070       default:
6071         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6072       }
6073     }
6074     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6075     /* Check permutation */
6076     {
6077       PetscInt *check;
6078 
6079       PetscCall(PetscMalloc1(size, &check));
6080       for (i = 0; i < size; ++i) {
6081         check[i] = -1;
6082         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6083       }
6084       for (i = 0; i < size; ++i) check[perm[i]] = i;
6085       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6086       PetscCall(PetscFree(check));
6087     }
6088     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6089     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6090       PetscInt *loc_perm;
6091       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6092       for (PetscInt i = 0; i < size; i++) {
6093         loc_perm[i]        = perm[i];
6094         loc_perm[size + i] = size + perm[i];
6095       }
6096       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6097     }
6098   }
6099   PetscFunctionReturn(PETSC_SUCCESS);
6100 }
6101 
6102 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6103 {
6104   PetscDS  prob;
6105   PetscInt depth, Nf, h;
6106   DMLabel  label;
6107 
6108   PetscFunctionBeginHot;
6109   PetscCall(DMGetDS(dm, &prob));
6110   Nf      = prob->Nf;
6111   label   = dm->depthLabel;
6112   *dspace = NULL;
6113   if (field < Nf) {
6114     PetscObject disc = prob->disc[field];
6115 
6116     if (disc->classid == PETSCFE_CLASSID) {
6117       PetscDualSpace dsp;
6118 
6119       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6120       PetscCall(DMLabelGetNumValues(label, &depth));
6121       PetscCall(DMLabelGetValue(label, point, &h));
6122       h = depth - 1 - h;
6123       if (h) {
6124         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6125       } else {
6126         *dspace = dsp;
6127       }
6128     }
6129   }
6130   PetscFunctionReturn(PETSC_SUCCESS);
6131 }
6132 
6133 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6134 {
6135   PetscScalar       *array;
6136   const PetscScalar *vArray;
6137   const PetscInt    *cone, *coneO;
6138   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6139 
6140   PetscFunctionBeginHot;
6141   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6142   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6143   PetscCall(DMPlexGetCone(dm, point, &cone));
6144   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6145   if (!values || !*values) {
6146     if ((point >= pStart) && (point < pEnd)) {
6147       PetscInt dof;
6148 
6149       PetscCall(PetscSectionGetDof(section, point, &dof));
6150       size += dof;
6151     }
6152     for (p = 0; p < numPoints; ++p) {
6153       const PetscInt cp = cone[p];
6154       PetscInt       dof;
6155 
6156       if ((cp < pStart) || (cp >= pEnd)) continue;
6157       PetscCall(PetscSectionGetDof(section, cp, &dof));
6158       size += dof;
6159     }
6160     if (!values) {
6161       if (csize) *csize = size;
6162       PetscFunctionReturn(PETSC_SUCCESS);
6163     }
6164     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6165   } else {
6166     array = *values;
6167   }
6168   size = 0;
6169   PetscCall(VecGetArrayRead(v, &vArray));
6170   if ((point >= pStart) && (point < pEnd)) {
6171     PetscInt           dof, off, d;
6172     const PetscScalar *varr;
6173 
6174     PetscCall(PetscSectionGetDof(section, point, &dof));
6175     PetscCall(PetscSectionGetOffset(section, point, &off));
6176     varr = PetscSafePointerPlusOffset(vArray, off);
6177     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6178     size += dof;
6179   }
6180   for (p = 0; p < numPoints; ++p) {
6181     const PetscInt     cp = cone[p];
6182     PetscInt           o  = coneO[p];
6183     PetscInt           dof, off, d;
6184     const PetscScalar *varr;
6185 
6186     if ((cp < pStart) || (cp >= pEnd)) continue;
6187     PetscCall(PetscSectionGetDof(section, cp, &dof));
6188     PetscCall(PetscSectionGetOffset(section, cp, &off));
6189     varr = PetscSafePointerPlusOffset(vArray, off);
6190     if (o >= 0) {
6191       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6192     } else {
6193       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6194     }
6195     size += dof;
6196   }
6197   PetscCall(VecRestoreArrayRead(v, &vArray));
6198   if (!*values) {
6199     if (csize) *csize = size;
6200     *values = array;
6201   } else {
6202     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6203     *csize = size;
6204   }
6205   PetscFunctionReturn(PETSC_SUCCESS);
6206 }
6207 
6208 /* Compress out points not in the section */
6209 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6210 {
6211   const PetscInt np = *numPoints;
6212   PetscInt       pStart, pEnd, p, q;
6213 
6214   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6215   for (p = 0, q = 0; p < np; ++p) {
6216     const PetscInt r = points[p * 2];
6217     if ((r >= pStart) && (r < pEnd)) {
6218       points[q * 2]     = r;
6219       points[q * 2 + 1] = points[p * 2 + 1];
6220       ++q;
6221     }
6222   }
6223   *numPoints = q;
6224   return PETSC_SUCCESS;
6225 }
6226 
6227 /* Compressed closure does not apply closure permutation */
6228 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6229 {
6230   const PetscInt *cla = NULL;
6231   PetscInt        np, *pts = NULL;
6232 
6233   PetscFunctionBeginHot;
6234   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6235   if (!ornt && *clPoints) {
6236     PetscInt dof, off;
6237 
6238     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6239     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6240     PetscCall(ISGetIndices(*clPoints, &cla));
6241     np  = dof / 2;
6242     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6243   } else {
6244     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6245     PetscCall(CompressPoints_Private(section, &np, pts));
6246   }
6247   *numPoints = np;
6248   *points    = pts;
6249   *clp       = cla;
6250   PetscFunctionReturn(PETSC_SUCCESS);
6251 }
6252 
6253 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6254 {
6255   PetscFunctionBeginHot;
6256   if (!*clPoints) {
6257     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6258   } else {
6259     PetscCall(ISRestoreIndices(*clPoints, clp));
6260   }
6261   *numPoints = 0;
6262   *points    = NULL;
6263   *clSec     = NULL;
6264   *clPoints  = NULL;
6265   *clp       = NULL;
6266   PetscFunctionReturn(PETSC_SUCCESS);
6267 }
6268 
6269 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6270 {
6271   PetscInt            offset = 0, p;
6272   const PetscInt    **perms  = NULL;
6273   const PetscScalar **flips  = NULL;
6274 
6275   PetscFunctionBeginHot;
6276   *size = 0;
6277   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6278   for (p = 0; p < numPoints; p++) {
6279     const PetscInt     point = points[2 * p];
6280     const PetscInt    *perm  = perms ? perms[p] : NULL;
6281     const PetscScalar *flip  = flips ? flips[p] : NULL;
6282     PetscInt           dof, off, d;
6283     const PetscScalar *varr;
6284 
6285     PetscCall(PetscSectionGetDof(section, point, &dof));
6286     PetscCall(PetscSectionGetOffset(section, point, &off));
6287     varr = PetscSafePointerPlusOffset(vArray, off);
6288     if (clperm) {
6289       if (perm) {
6290         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6291       } else {
6292         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6293       }
6294       if (flip) {
6295         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6296       }
6297     } else {
6298       if (perm) {
6299         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6300       } else {
6301         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6302       }
6303       if (flip) {
6304         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6305       }
6306     }
6307     offset += dof;
6308   }
6309   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6310   *size = offset;
6311   PetscFunctionReturn(PETSC_SUCCESS);
6312 }
6313 
6314 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[])
6315 {
6316   PetscInt offset = 0, f;
6317 
6318   PetscFunctionBeginHot;
6319   *size = 0;
6320   for (f = 0; f < numFields; ++f) {
6321     PetscInt            p;
6322     const PetscInt    **perms = NULL;
6323     const PetscScalar **flips = NULL;
6324 
6325     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6326     for (p = 0; p < numPoints; p++) {
6327       const PetscInt     point = points[2 * p];
6328       PetscInt           fdof, foff, b;
6329       const PetscScalar *varr;
6330       const PetscInt    *perm = perms ? perms[p] : NULL;
6331       const PetscScalar *flip = flips ? flips[p] : NULL;
6332 
6333       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6334       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6335       varr = &vArray[foff];
6336       if (clperm) {
6337         if (perm) {
6338           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6339         } else {
6340           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6341         }
6342         if (flip) {
6343           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6344         }
6345       } else {
6346         if (perm) {
6347           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6348         } else {
6349           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6350         }
6351         if (flip) {
6352           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6353         }
6354       }
6355       offset += fdof;
6356     }
6357     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6358   }
6359   *size = offset;
6360   PetscFunctionReturn(PETSC_SUCCESS);
6361 }
6362 
6363 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6364 {
6365   PetscSection    clSection;
6366   IS              clPoints;
6367   PetscInt       *points = NULL;
6368   const PetscInt *clp, *perm = NULL;
6369   PetscInt        depth, numFields, numPoints, asize;
6370 
6371   PetscFunctionBeginHot;
6372   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6373   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6374   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6375   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6376   PetscCall(DMPlexGetDepth(dm, &depth));
6377   PetscCall(PetscSectionGetNumFields(section, &numFields));
6378   if (depth == 1 && numFields < 2) {
6379     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6380     PetscFunctionReturn(PETSC_SUCCESS);
6381   }
6382   /* Get points */
6383   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6384   /* Get sizes */
6385   asize = 0;
6386   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6387     PetscInt dof;
6388     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6389     asize += dof;
6390   }
6391   if (values) {
6392     const PetscScalar *vArray;
6393     PetscInt           size;
6394 
6395     if (*values) {
6396       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);
6397     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6398     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6399     PetscCall(VecGetArrayRead(v, &vArray));
6400     /* Get values */
6401     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6402     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6403     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6404     /* Cleanup array */
6405     PetscCall(VecRestoreArrayRead(v, &vArray));
6406   }
6407   if (csize) *csize = asize;
6408   /* Cleanup points */
6409   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6410   PetscFunctionReturn(PETSC_SUCCESS);
6411 }
6412 
6413 /*@C
6414   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6415 
6416   Not collective
6417 
6418   Input Parameters:
6419 + dm      - The `DM`
6420 . section - The section describing the layout in `v`, or `NULL` to use the default section
6421 . v       - The local vector
6422 - point   - The point in the `DM`
6423 
6424   Input/Output Parameters:
6425 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6426 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6427            if the user provided `NULL`, it is a borrowed array and should not be freed
6428 
6429   Level: intermediate
6430 
6431   Notes:
6432   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6433   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6434   assembly function, and a user may already have allocated storage for this operation.
6435 
6436   A typical use could be
6437 .vb
6438    values = NULL;
6439    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6440    for (cl = 0; cl < clSize; ++cl) {
6441      <Compute on closure>
6442    }
6443    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6444 .ve
6445   or
6446 .vb
6447    PetscMalloc1(clMaxSize, &values);
6448    for (p = pStart; p < pEnd; ++p) {
6449      clSize = clMaxSize;
6450      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6451      for (cl = 0; cl < clSize; ++cl) {
6452        <Compute on closure>
6453      }
6454    }
6455    PetscFree(values);
6456 .ve
6457 
6458   Fortran Notes:
6459   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6460 
6461   `values` must be declared with
6462 .vb
6463   PetscScalar,dimension(:),pointer   :: values
6464 .ve
6465   and it will be allocated internally by PETSc to hold the values returned
6466 
6467 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6468 @*/
6469 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6470 {
6471   PetscFunctionBeginHot;
6472   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6473   PetscFunctionReturn(PETSC_SUCCESS);
6474 }
6475 
6476 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6477 {
6478   DMLabel            depthLabel;
6479   PetscSection       clSection;
6480   IS                 clPoints;
6481   PetscScalar       *array;
6482   const PetscScalar *vArray;
6483   PetscInt          *points = NULL;
6484   const PetscInt    *clp, *perm = NULL;
6485   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6486 
6487   PetscFunctionBeginHot;
6488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6489   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6490   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6491   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6492   PetscCall(DMPlexGetDepth(dm, &mdepth));
6493   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6494   PetscCall(PetscSectionGetNumFields(section, &numFields));
6495   if (mdepth == 1 && numFields < 2) {
6496     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6497     PetscFunctionReturn(PETSC_SUCCESS);
6498   }
6499   /* Get points */
6500   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6501   for (clsize = 0, p = 0; p < Np; p++) {
6502     PetscInt dof;
6503     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6504     clsize += dof;
6505   }
6506   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6507   /* Filter points */
6508   for (p = 0; p < numPoints * 2; p += 2) {
6509     PetscInt dep;
6510 
6511     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6512     if (dep != depth) continue;
6513     points[Np * 2 + 0] = points[p];
6514     points[Np * 2 + 1] = points[p + 1];
6515     ++Np;
6516   }
6517   /* Get array */
6518   if (!values || !*values) {
6519     PetscInt asize = 0, dof;
6520 
6521     for (p = 0; p < Np * 2; p += 2) {
6522       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6523       asize += dof;
6524     }
6525     if (!values) {
6526       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6527       if (csize) *csize = asize;
6528       PetscFunctionReturn(PETSC_SUCCESS);
6529     }
6530     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6531   } else {
6532     array = *values;
6533   }
6534   PetscCall(VecGetArrayRead(v, &vArray));
6535   /* Get values */
6536   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6537   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6538   /* Cleanup points */
6539   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6540   /* Cleanup array */
6541   PetscCall(VecRestoreArrayRead(v, &vArray));
6542   if (!*values) {
6543     if (csize) *csize = size;
6544     *values = array;
6545   } else {
6546     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6547     *csize = size;
6548   }
6549   PetscFunctionReturn(PETSC_SUCCESS);
6550 }
6551 
6552 /*@C
6553   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6554 
6555   Not collective
6556 
6557   Input Parameters:
6558 + dm      - The `DM`
6559 . section - The section describing the layout in `v`, or `NULL` to use the default section
6560 . v       - The local vector
6561 . point   - The point in the `DM`
6562 . csize   - The number of values in the closure, or `NULL`
6563 - values  - The array of values
6564 
6565   Level: intermediate
6566 
6567   Note:
6568   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6569 
6570   Fortran Note:
6571   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6572 
6573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6574 @*/
6575 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6576 {
6577   PetscInt size = 0;
6578 
6579   PetscFunctionBegin;
6580   /* Should work without recalculating size */
6581   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6582   *values = NULL;
6583   PetscFunctionReturn(PETSC_SUCCESS);
6584 }
6585 
6586 static inline void add(PetscScalar *x, PetscScalar y)
6587 {
6588   *x += y;
6589 }
6590 static inline void insert(PetscScalar *x, PetscScalar y)
6591 {
6592   *x = y;
6593 }
6594 
6595 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[])
6596 {
6597   PetscInt        cdof;  /* The number of constraints on this point */
6598   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6599   PetscScalar    *a;
6600   PetscInt        off, cind = 0, k;
6601 
6602   PetscFunctionBegin;
6603   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6604   PetscCall(PetscSectionGetOffset(section, point, &off));
6605   a = &array[off];
6606   if (!cdof || setBC) {
6607     if (clperm) {
6608       if (perm) {
6609         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6610       } else {
6611         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6612       }
6613     } else {
6614       if (perm) {
6615         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6616       } else {
6617         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6618       }
6619     }
6620   } else {
6621     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6622     if (clperm) {
6623       if (perm) {
6624         for (k = 0; k < dof; ++k) {
6625           if ((cind < cdof) && (k == cdofs[cind])) {
6626             ++cind;
6627             continue;
6628           }
6629           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6630         }
6631       } else {
6632         for (k = 0; k < dof; ++k) {
6633           if ((cind < cdof) && (k == cdofs[cind])) {
6634             ++cind;
6635             continue;
6636           }
6637           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6638         }
6639       }
6640     } else {
6641       if (perm) {
6642         for (k = 0; k < dof; ++k) {
6643           if ((cind < cdof) && (k == cdofs[cind])) {
6644             ++cind;
6645             continue;
6646           }
6647           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6648         }
6649       } else {
6650         for (k = 0; k < dof; ++k) {
6651           if ((cind < cdof) && (k == cdofs[cind])) {
6652             ++cind;
6653             continue;
6654           }
6655           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6656         }
6657       }
6658     }
6659   }
6660   PetscFunctionReturn(PETSC_SUCCESS);
6661 }
6662 
6663 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[])
6664 {
6665   PetscInt        cdof;  /* The number of constraints on this point */
6666   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6667   PetscScalar    *a;
6668   PetscInt        off, cind = 0, k;
6669 
6670   PetscFunctionBegin;
6671   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6672   PetscCall(PetscSectionGetOffset(section, point, &off));
6673   a = &array[off];
6674   if (cdof) {
6675     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6676     if (clperm) {
6677       if (perm) {
6678         for (k = 0; k < dof; ++k) {
6679           if ((cind < cdof) && (k == cdofs[cind])) {
6680             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6681             cind++;
6682           }
6683         }
6684       } else {
6685         for (k = 0; k < dof; ++k) {
6686           if ((cind < cdof) && (k == cdofs[cind])) {
6687             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6688             cind++;
6689           }
6690         }
6691       }
6692     } else {
6693       if (perm) {
6694         for (k = 0; k < dof; ++k) {
6695           if ((cind < cdof) && (k == cdofs[cind])) {
6696             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6697             cind++;
6698           }
6699         }
6700       } else {
6701         for (k = 0; k < dof; ++k) {
6702           if ((cind < cdof) && (k == cdofs[cind])) {
6703             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6704             cind++;
6705           }
6706         }
6707       }
6708     }
6709   }
6710   PetscFunctionReturn(PETSC_SUCCESS);
6711 }
6712 
6713 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[])
6714 {
6715   PetscScalar    *a;
6716   PetscInt        fdof, foff, fcdof, foffset = *offset;
6717   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6718   PetscInt        cind = 0, b;
6719 
6720   PetscFunctionBegin;
6721   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6722   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6723   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6724   a = &array[foff];
6725   if (!fcdof || setBC) {
6726     if (clperm) {
6727       if (perm) {
6728         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6729       } else {
6730         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6731       }
6732     } else {
6733       if (perm) {
6734         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6735       } else {
6736         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6737       }
6738     }
6739   } else {
6740     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6741     if (clperm) {
6742       if (perm) {
6743         for (b = 0; b < fdof; b++) {
6744           if ((cind < fcdof) && (b == fcdofs[cind])) {
6745             ++cind;
6746             continue;
6747           }
6748           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6749         }
6750       } else {
6751         for (b = 0; b < fdof; b++) {
6752           if ((cind < fcdof) && (b == fcdofs[cind])) {
6753             ++cind;
6754             continue;
6755           }
6756           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6757         }
6758       }
6759     } else {
6760       if (perm) {
6761         for (b = 0; b < fdof; b++) {
6762           if ((cind < fcdof) && (b == fcdofs[cind])) {
6763             ++cind;
6764             continue;
6765           }
6766           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6767         }
6768       } else {
6769         for (b = 0; b < fdof; b++) {
6770           if ((cind < fcdof) && (b == fcdofs[cind])) {
6771             ++cind;
6772             continue;
6773           }
6774           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6775         }
6776       }
6777     }
6778   }
6779   *offset += fdof;
6780   PetscFunctionReturn(PETSC_SUCCESS);
6781 }
6782 
6783 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[])
6784 {
6785   PetscScalar    *a;
6786   PetscInt        fdof, foff, fcdof, foffset = *offset;
6787   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6788   PetscInt        Nc, cind = 0, ncind = 0, b;
6789   PetscBool       ncSet, fcSet;
6790 
6791   PetscFunctionBegin;
6792   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6793   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6794   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6795   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6796   a = &array[foff];
6797   if (fcdof) {
6798     /* We just override fcdof and fcdofs with Ncc and comps */
6799     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6800     if (clperm) {
6801       if (perm) {
6802         if (comps) {
6803           for (b = 0; b < fdof; b++) {
6804             ncSet = fcSet = PETSC_FALSE;
6805             if (b % Nc == comps[ncind]) {
6806               ncind = (ncind + 1) % Ncc;
6807               ncSet = PETSC_TRUE;
6808             }
6809             if ((cind < fcdof) && (b == fcdofs[cind])) {
6810               ++cind;
6811               fcSet = PETSC_TRUE;
6812             }
6813             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6814           }
6815         } else {
6816           for (b = 0; b < fdof; b++) {
6817             if ((cind < fcdof) && (b == fcdofs[cind])) {
6818               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6819               ++cind;
6820             }
6821           }
6822         }
6823       } else {
6824         if (comps) {
6825           for (b = 0; b < fdof; b++) {
6826             ncSet = fcSet = PETSC_FALSE;
6827             if (b % Nc == comps[ncind]) {
6828               ncind = (ncind + 1) % Ncc;
6829               ncSet = PETSC_TRUE;
6830             }
6831             if ((cind < fcdof) && (b == fcdofs[cind])) {
6832               ++cind;
6833               fcSet = PETSC_TRUE;
6834             }
6835             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6836           }
6837         } else {
6838           for (b = 0; b < fdof; b++) {
6839             if ((cind < fcdof) && (b == fcdofs[cind])) {
6840               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6841               ++cind;
6842             }
6843           }
6844         }
6845       }
6846     } else {
6847       if (perm) {
6848         if (comps) {
6849           for (b = 0; b < fdof; b++) {
6850             ncSet = fcSet = PETSC_FALSE;
6851             if (b % Nc == comps[ncind]) {
6852               ncind = (ncind + 1) % Ncc;
6853               ncSet = PETSC_TRUE;
6854             }
6855             if ((cind < fcdof) && (b == fcdofs[cind])) {
6856               ++cind;
6857               fcSet = PETSC_TRUE;
6858             }
6859             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6860           }
6861         } else {
6862           for (b = 0; b < fdof; b++) {
6863             if ((cind < fcdof) && (b == fcdofs[cind])) {
6864               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6865               ++cind;
6866             }
6867           }
6868         }
6869       } else {
6870         if (comps) {
6871           for (b = 0; b < fdof; b++) {
6872             ncSet = fcSet = PETSC_FALSE;
6873             if (b % Nc == comps[ncind]) {
6874               ncind = (ncind + 1) % Ncc;
6875               ncSet = PETSC_TRUE;
6876             }
6877             if ((cind < fcdof) && (b == fcdofs[cind])) {
6878               ++cind;
6879               fcSet = PETSC_TRUE;
6880             }
6881             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6882           }
6883         } else {
6884           for (b = 0; b < fdof; b++) {
6885             if ((cind < fcdof) && (b == fcdofs[cind])) {
6886               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6887               ++cind;
6888             }
6889           }
6890         }
6891       }
6892     }
6893   }
6894   *offset += fdof;
6895   PetscFunctionReturn(PETSC_SUCCESS);
6896 }
6897 
6898 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6899 {
6900   PetscScalar    *array;
6901   const PetscInt *cone, *coneO;
6902   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6903 
6904   PetscFunctionBeginHot;
6905   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6906   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6907   PetscCall(DMPlexGetCone(dm, point, &cone));
6908   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6909   PetscCall(VecGetArray(v, &array));
6910   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6911     const PetscInt cp = !p ? point : cone[p - 1];
6912     const PetscInt o  = !p ? 0 : coneO[p - 1];
6913 
6914     if ((cp < pStart) || (cp >= pEnd)) {
6915       dof = 0;
6916       continue;
6917     }
6918     PetscCall(PetscSectionGetDof(section, cp, &dof));
6919     /* ADD_VALUES */
6920     {
6921       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6922       PetscScalar    *a;
6923       PetscInt        cdof, coff, cind = 0, k;
6924 
6925       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6926       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6927       a = &array[coff];
6928       if (!cdof) {
6929         if (o >= 0) {
6930           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6931         } else {
6932           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6933         }
6934       } else {
6935         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6936         if (o >= 0) {
6937           for (k = 0; k < dof; ++k) {
6938             if ((cind < cdof) && (k == cdofs[cind])) {
6939               ++cind;
6940               continue;
6941             }
6942             a[k] += values[off + k];
6943           }
6944         } else {
6945           for (k = 0; k < dof; ++k) {
6946             if ((cind < cdof) && (k == cdofs[cind])) {
6947               ++cind;
6948               continue;
6949             }
6950             a[k] += values[off + dof - k - 1];
6951           }
6952         }
6953       }
6954     }
6955   }
6956   PetscCall(VecRestoreArray(v, &array));
6957   PetscFunctionReturn(PETSC_SUCCESS);
6958 }
6959 
6960 /*@C
6961   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6962 
6963   Not collective
6964 
6965   Input Parameters:
6966 + dm      - The `DM`
6967 . section - The section describing the layout in `v`, or `NULL` to use the default section
6968 . v       - The local vector
6969 . point   - The point in the `DM`
6970 . values  - The array of values
6971 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6972             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6973 
6974   Level: intermediate
6975 
6976   Note:
6977   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
6978 
6979   Fortran Note:
6980   `values` must be declared with
6981 .vb
6982   PetscScalar,dimension(:),pointer   :: values
6983 .ve
6984 
6985 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6986 @*/
6987 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6988 {
6989   PetscSection    clSection;
6990   IS              clPoints;
6991   PetscScalar    *array;
6992   PetscInt       *points = NULL;
6993   const PetscInt *clp, *clperm = NULL;
6994   PetscInt        depth, numFields, numPoints, p, clsize;
6995 
6996   PetscFunctionBeginHot;
6997   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6998   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6999   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7000   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7001   PetscCall(DMPlexGetDepth(dm, &depth));
7002   PetscCall(PetscSectionGetNumFields(section, &numFields));
7003   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7004     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7005     PetscFunctionReturn(PETSC_SUCCESS);
7006   }
7007   /* Get points */
7008   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7009   for (clsize = 0, p = 0; p < numPoints; p++) {
7010     PetscInt dof;
7011     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7012     clsize += dof;
7013   }
7014   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7015   /* Get array */
7016   PetscCall(VecGetArray(v, &array));
7017   /* Get values */
7018   if (numFields > 0) {
7019     PetscInt offset = 0, f;
7020     for (f = 0; f < numFields; ++f) {
7021       const PetscInt    **perms = NULL;
7022       const PetscScalar **flips = NULL;
7023 
7024       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7025       switch (mode) {
7026       case INSERT_VALUES:
7027         for (p = 0; p < numPoints; p++) {
7028           const PetscInt     point = points[2 * p];
7029           const PetscInt    *perm  = perms ? perms[p] : NULL;
7030           const PetscScalar *flip  = flips ? flips[p] : NULL;
7031           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7032         }
7033         break;
7034       case INSERT_ALL_VALUES:
7035         for (p = 0; p < numPoints; p++) {
7036           const PetscInt     point = points[2 * p];
7037           const PetscInt    *perm  = perms ? perms[p] : NULL;
7038           const PetscScalar *flip  = flips ? flips[p] : NULL;
7039           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7040         }
7041         break;
7042       case INSERT_BC_VALUES:
7043         for (p = 0; p < numPoints; p++) {
7044           const PetscInt     point = points[2 * p];
7045           const PetscInt    *perm  = perms ? perms[p] : NULL;
7046           const PetscScalar *flip  = flips ? flips[p] : NULL;
7047           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7048         }
7049         break;
7050       case ADD_VALUES:
7051         for (p = 0; p < numPoints; p++) {
7052           const PetscInt     point = points[2 * p];
7053           const PetscInt    *perm  = perms ? perms[p] : NULL;
7054           const PetscScalar *flip  = flips ? flips[p] : NULL;
7055           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7056         }
7057         break;
7058       case ADD_ALL_VALUES:
7059         for (p = 0; p < numPoints; p++) {
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(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7064         }
7065         break;
7066       case ADD_BC_VALUES:
7067         for (p = 0; p < numPoints; p++) {
7068           const PetscInt     point = points[2 * p];
7069           const PetscInt    *perm  = perms ? perms[p] : NULL;
7070           const PetscScalar *flip  = flips ? flips[p] : NULL;
7071           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7072         }
7073         break;
7074       default:
7075         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7076       }
7077       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7078     }
7079   } else {
7080     PetscInt            dof, off;
7081     const PetscInt    **perms = NULL;
7082     const PetscScalar **flips = NULL;
7083 
7084     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7085     switch (mode) {
7086     case INSERT_VALUES:
7087       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7088         const PetscInt     point = points[2 * p];
7089         const PetscInt    *perm  = perms ? perms[p] : NULL;
7090         const PetscScalar *flip  = flips ? flips[p] : NULL;
7091         PetscCall(PetscSectionGetDof(section, point, &dof));
7092         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7093       }
7094       break;
7095     case INSERT_ALL_VALUES:
7096       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7097         const PetscInt     point = points[2 * p];
7098         const PetscInt    *perm  = perms ? perms[p] : NULL;
7099         const PetscScalar *flip  = flips ? flips[p] : NULL;
7100         PetscCall(PetscSectionGetDof(section, point, &dof));
7101         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7102       }
7103       break;
7104     case INSERT_BC_VALUES:
7105       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7106         const PetscInt     point = points[2 * p];
7107         const PetscInt    *perm  = perms ? perms[p] : NULL;
7108         const PetscScalar *flip  = flips ? flips[p] : NULL;
7109         PetscCall(PetscSectionGetDof(section, point, &dof));
7110         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7111       }
7112       break;
7113     case ADD_VALUES:
7114       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7115         const PetscInt     point = points[2 * p];
7116         const PetscInt    *perm  = perms ? perms[p] : NULL;
7117         const PetscScalar *flip  = flips ? flips[p] : NULL;
7118         PetscCall(PetscSectionGetDof(section, point, &dof));
7119         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7120       }
7121       break;
7122     case ADD_ALL_VALUES:
7123       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7124         const PetscInt     point = points[2 * p];
7125         const PetscInt    *perm  = perms ? perms[p] : NULL;
7126         const PetscScalar *flip  = flips ? flips[p] : NULL;
7127         PetscCall(PetscSectionGetDof(section, point, &dof));
7128         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7129       }
7130       break;
7131     case ADD_BC_VALUES:
7132       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7133         const PetscInt     point = points[2 * p];
7134         const PetscInt    *perm  = perms ? perms[p] : NULL;
7135         const PetscScalar *flip  = flips ? flips[p] : NULL;
7136         PetscCall(PetscSectionGetDof(section, point, &dof));
7137         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7138       }
7139       break;
7140     default:
7141       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7142     }
7143     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7144   }
7145   /* Cleanup points */
7146   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7147   /* Cleanup array */
7148   PetscCall(VecRestoreArray(v, &array));
7149   PetscFunctionReturn(PETSC_SUCCESS);
7150 }
7151 
7152 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7153 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7154 {
7155   PetscFunctionBegin;
7156   *contains = PETSC_TRUE;
7157   if (label) {
7158     PetscInt fdof;
7159 
7160     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7161     if (!*contains) {
7162       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7163       *offset += fdof;
7164       PetscFunctionReturn(PETSC_SUCCESS);
7165     }
7166   }
7167   PetscFunctionReturn(PETSC_SUCCESS);
7168 }
7169 
7170 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7171 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)
7172 {
7173   PetscSection    clSection;
7174   IS              clPoints;
7175   PetscScalar    *array;
7176   PetscInt       *points = NULL;
7177   const PetscInt *clp;
7178   PetscInt        numFields, numPoints, p;
7179   PetscInt        offset = 0, f;
7180 
7181   PetscFunctionBeginHot;
7182   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7183   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7184   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7185   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7186   PetscCall(PetscSectionGetNumFields(section, &numFields));
7187   /* Get points */
7188   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7189   /* Get array */
7190   PetscCall(VecGetArray(v, &array));
7191   /* Get values */
7192   for (f = 0; f < numFields; ++f) {
7193     const PetscInt    **perms = NULL;
7194     const PetscScalar **flips = NULL;
7195     PetscBool           contains;
7196 
7197     if (!fieldActive[f]) {
7198       for (p = 0; p < numPoints * 2; p += 2) {
7199         PetscInt fdof;
7200         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7201         offset += fdof;
7202       }
7203       continue;
7204     }
7205     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7206     switch (mode) {
7207     case INSERT_VALUES:
7208       for (p = 0; p < numPoints; p++) {
7209         const PetscInt     point = points[2 * p];
7210         const PetscInt    *perm  = perms ? perms[p] : NULL;
7211         const PetscScalar *flip  = flips ? flips[p] : NULL;
7212         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7213         if (!contains) continue;
7214         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7215       }
7216       break;
7217     case INSERT_ALL_VALUES:
7218       for (p = 0; p < numPoints; p++) {
7219         const PetscInt     point = points[2 * p];
7220         const PetscInt    *perm  = perms ? perms[p] : NULL;
7221         const PetscScalar *flip  = flips ? flips[p] : NULL;
7222         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7223         if (!contains) continue;
7224         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7225       }
7226       break;
7227     case INSERT_BC_VALUES:
7228       for (p = 0; p < numPoints; p++) {
7229         const PetscInt     point = points[2 * p];
7230         const PetscInt    *perm  = perms ? perms[p] : NULL;
7231         const PetscScalar *flip  = flips ? flips[p] : NULL;
7232         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7233         if (!contains) continue;
7234         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7235       }
7236       break;
7237     case ADD_VALUES:
7238       for (p = 0; p < numPoints; p++) {
7239         const PetscInt     point = points[2 * p];
7240         const PetscInt    *perm  = perms ? perms[p] : NULL;
7241         const PetscScalar *flip  = flips ? flips[p] : NULL;
7242         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7243         if (!contains) continue;
7244         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7245       }
7246       break;
7247     case ADD_ALL_VALUES:
7248       for (p = 0; p < numPoints; p++) {
7249         const PetscInt     point = points[2 * p];
7250         const PetscInt    *perm  = perms ? perms[p] : NULL;
7251         const PetscScalar *flip  = flips ? flips[p] : NULL;
7252         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7253         if (!contains) continue;
7254         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7255       }
7256       break;
7257     default:
7258       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7259     }
7260     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7261   }
7262   /* Cleanup points */
7263   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7264   /* Cleanup array */
7265   PetscCall(VecRestoreArray(v, &array));
7266   PetscFunctionReturn(PETSC_SUCCESS);
7267 }
7268 
7269 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7270 {
7271   PetscMPIInt rank;
7272   PetscInt    i, j;
7273 
7274   PetscFunctionBegin;
7275   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7276   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7277   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7278   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7279   numCIndices = numCIndices ? numCIndices : numRIndices;
7280   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7281   for (i = 0; i < numRIndices; i++) {
7282     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7283     for (j = 0; j < numCIndices; j++) {
7284 #if defined(PETSC_USE_COMPLEX)
7285       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7286 #else
7287       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7288 #endif
7289     }
7290     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7291   }
7292   PetscFunctionReturn(PETSC_SUCCESS);
7293 }
7294 
7295 /*
7296   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7297 
7298   Input Parameters:
7299 + section - The section for this data layout
7300 . islocal - Is the section (and thus indices being requested) local or global?
7301 . point   - The point contributing dofs with these indices
7302 . off     - The global offset of this point
7303 . loff    - The local offset of each field
7304 . setBC   - The flag determining whether to include indices of boundary values
7305 . perm    - A permutation of the dofs on this point, or NULL
7306 - indperm - A permutation of the entire indices array, or NULL
7307 
7308   Output Parameter:
7309 . indices - Indices for dofs on this point
7310 
7311   Level: developer
7312 
7313   Note: The indices could be local or global, depending on the value of 'off'.
7314 */
7315 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7316 {
7317   PetscInt        dof;   /* The number of unknowns on this point */
7318   PetscInt        cdof;  /* The number of constraints on this point */
7319   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7320   PetscInt        cind = 0, k;
7321 
7322   PetscFunctionBegin;
7323   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7324   PetscCall(PetscSectionGetDof(section, point, &dof));
7325   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7326   if (!cdof || setBC) {
7327     for (k = 0; k < dof; ++k) {
7328       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7329       const PetscInt ind    = indperm ? indperm[preind] : preind;
7330 
7331       indices[ind] = off + k;
7332     }
7333   } else {
7334     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7335     for (k = 0; k < dof; ++k) {
7336       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7337       const PetscInt ind    = indperm ? indperm[preind] : preind;
7338 
7339       if ((cind < cdof) && (k == cdofs[cind])) {
7340         /* Insert check for returning constrained indices */
7341         indices[ind] = -(off + k + 1);
7342         ++cind;
7343       } else {
7344         indices[ind] = off + k - (islocal ? 0 : cind);
7345       }
7346     }
7347   }
7348   *loff += dof;
7349   PetscFunctionReturn(PETSC_SUCCESS);
7350 }
7351 
7352 /*
7353  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7354 
7355  Input Parameters:
7356 + section - a section (global or local)
7357 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7358 . point - point within section
7359 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7360 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7361 . setBC - identify constrained (boundary condition) points via involution.
7362 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7363 . permsoff - offset
7364 - indperm - index permutation
7365 
7366  Output Parameter:
7367 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7368 . indices - array to hold indices (as defined by section) of each dof associated with point
7369 
7370  Notes:
7371  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7372  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7373  in the local vector.
7374 
7375  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7376  significant).  It is invalid to call with a global section and setBC=true.
7377 
7378  Developer Note:
7379  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7380  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7381  offset could be obtained from the section instead of passing it explicitly as we do now.
7382 
7383  Example:
7384  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7385  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7386  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7387  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.
7388 
7389  Level: developer
7390 */
7391 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[])
7392 {
7393   PetscInt numFields, foff, f;
7394 
7395   PetscFunctionBegin;
7396   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7397   PetscCall(PetscSectionGetNumFields(section, &numFields));
7398   for (f = 0, foff = 0; f < numFields; ++f) {
7399     PetscInt        fdof, cfdof;
7400     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7401     PetscInt        cind = 0, b;
7402     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7403 
7404     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7405     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7406     if (!cfdof || setBC) {
7407       for (b = 0; b < fdof; ++b) {
7408         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7409         const PetscInt ind    = indperm ? indperm[preind] : preind;
7410 
7411         indices[ind] = off + foff + b;
7412       }
7413     } else {
7414       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7415       for (b = 0; b < fdof; ++b) {
7416         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7417         const PetscInt ind    = indperm ? indperm[preind] : preind;
7418 
7419         if ((cind < cfdof) && (b == fcdofs[cind])) {
7420           indices[ind] = -(off + foff + b + 1);
7421           ++cind;
7422         } else {
7423           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7424         }
7425       }
7426     }
7427     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7428     foffs[f] += fdof;
7429   }
7430   PetscFunctionReturn(PETSC_SUCCESS);
7431 }
7432 
7433 /*
7434   This version believes the globalSection offsets for each field, rather than just the point offset
7435 
7436  . foffs - The offset into 'indices' for each field, since it is segregated by field
7437 
7438  Notes:
7439  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7440  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7441 */
7442 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7443 {
7444   PetscInt numFields, foff, f;
7445 
7446   PetscFunctionBegin;
7447   PetscCall(PetscSectionGetNumFields(section, &numFields));
7448   for (f = 0; f < numFields; ++f) {
7449     PetscInt        fdof, cfdof;
7450     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7451     PetscInt        cind = 0, b;
7452     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7453 
7454     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7455     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7456     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7457     if (!cfdof) {
7458       for (b = 0; b < fdof; ++b) {
7459         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7460         const PetscInt ind    = indperm ? indperm[preind] : preind;
7461 
7462         indices[ind] = foff + b;
7463       }
7464     } else {
7465       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7466       for (b = 0; b < fdof; ++b) {
7467         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7468         const PetscInt ind    = indperm ? indperm[preind] : preind;
7469 
7470         if ((cind < cfdof) && (b == fcdofs[cind])) {
7471           indices[ind] = -(foff + b + 1);
7472           ++cind;
7473         } else {
7474           indices[ind] = foff + b - cind;
7475         }
7476       }
7477     }
7478     foffs[f] += fdof;
7479   }
7480   PetscFunctionReturn(PETSC_SUCCESS);
7481 }
7482 
7483 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7484 {
7485   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7486 
7487   PetscFunctionBegin;
7488   PetscCall(PetscSectionGetNumFields(section, &numFields));
7489   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7490   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7491   for (PetscInt p = 0; p < nPoints; p++) {
7492     PetscInt     b       = pnts[2 * p];
7493     PetscInt     bSecDof = 0, bOff;
7494     PetscInt     cSecDof = 0;
7495     PetscSection indices_section;
7496 
7497     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7498     if (!bSecDof) continue;
7499     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7500     indices_section = cSecDof > 0 ? cSec : section;
7501     if (numFields) {
7502       PetscInt fStart[32], fEnd[32];
7503 
7504       fStart[0] = 0;
7505       fEnd[0]   = 0;
7506       for (PetscInt f = 0; f < numFields; f++) {
7507         PetscInt fDof = 0;
7508 
7509         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7510         fStart[f + 1] = fStart[f] + fDof;
7511         fEnd[f + 1]   = fStart[f + 1];
7512       }
7513       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7514       // only apply permutations on one side
7515       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7516       for (PetscInt f = 0; f < numFields; f++) {
7517         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7518       }
7519     } else {
7520       PetscInt bEnd = 0;
7521 
7522       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7523       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7524 
7525       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7526     }
7527   }
7528   PetscFunctionReturn(PETSC_SUCCESS);
7529 }
7530 
7531 PETSC_INTERN PetscErrorCode DMPlexAnchorsGetSubMatModification(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscInt offsets[], PetscScalar *outMat[])
7532 {
7533   Mat             cMat;
7534   PetscSection    aSec, cSec;
7535   IS              aIS;
7536   PetscInt        aStart = -1, aEnd = -1;
7537   PetscInt        sStart = -1, sEnd = -1;
7538   PetscInt        cStart = -1, cEnd = -1;
7539   const PetscInt *anchors;
7540   PetscInt        numFields, p;
7541   PetscInt        newNumPoints = 0, newNumIndices = 0;
7542   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7543   PetscInt        oldOffsets[32];
7544   PetscInt        newOffsets[32];
7545   PetscInt        oldOffsetsCopy[32];
7546   PetscInt        newOffsetsCopy[32];
7547   PetscScalar    *modMat         = NULL;
7548   PetscBool       anyConstrained = PETSC_FALSE;
7549 
7550   PetscFunctionBegin;
7551   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7552   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7553   PetscCall(PetscSectionGetNumFields(section, &numFields));
7554 
7555   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7556   /* if there are point-to-point constraints */
7557   if (aSec) {
7558     PetscCall(PetscArrayzero(newOffsets, 32));
7559     PetscCall(PetscArrayzero(oldOffsets, 32));
7560     PetscCall(ISGetIndices(aIS, &anchors));
7561     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7562     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7563     /* figure out how many points are going to be in the new element matrix
7564      * (we allow double counting, because it's all just going to be summed
7565      * into the global matrix anyway) */
7566     for (p = 0; p < 2 * numPoints; p += 2) {
7567       PetscInt b    = points[p];
7568       PetscInt bDof = 0, bSecDof = 0;
7569 
7570       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7571       if (!bSecDof) continue;
7572 
7573       for (PetscInt f = 0; f < numFields; f++) {
7574         PetscInt fDof = 0;
7575 
7576         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7577         oldOffsets[f + 1] += fDof;
7578       }
7579       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7580       if (bDof) {
7581         /* this point is constrained */
7582         /* it is going to be replaced by its anchors */
7583         PetscInt bOff, q;
7584 
7585         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7586         for (q = 0; q < bDof; q++) {
7587           PetscInt a    = anchors[bOff + q];
7588           PetscInt aDof = 0;
7589 
7590           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7591           if (aDof) {
7592             anyConstrained = PETSC_TRUE;
7593             newNumPoints += 1;
7594           }
7595           newNumIndices += aDof;
7596           for (PetscInt f = 0; f < numFields; ++f) {
7597             PetscInt fDof = 0;
7598 
7599             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7600             newOffsets[f + 1] += fDof;
7601           }
7602         }
7603       } else {
7604         /* this point is not constrained */
7605         newNumPoints++;
7606         newNumIndices += bSecDof;
7607         for (PetscInt f = 0; f < numFields; ++f) {
7608           PetscInt fDof;
7609 
7610           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7611           newOffsets[f + 1] += fDof;
7612         }
7613       }
7614     }
7615   }
7616   if (!anyConstrained) {
7617     if (outNumPoints) *outNumPoints = 0;
7618     if (outNumIndices) *outNumIndices = 0;
7619     if (outPoints) *outPoints = NULL;
7620     if (outMat) *outMat = NULL;
7621     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7622     PetscFunctionReturn(PETSC_SUCCESS);
7623   }
7624 
7625   if (outNumPoints) *outNumPoints = newNumPoints;
7626   if (outNumIndices) *outNumIndices = newNumIndices;
7627 
7628   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7629   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7630 
7631   if (!outPoints && !outMat) {
7632     if (offsets) {
7633       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7634     }
7635     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7636     PetscFunctionReturn(PETSC_SUCCESS);
7637   }
7638 
7639   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7640   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7641 
7642   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7643   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7644 
7645   /* output arrays */
7646   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7647   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7648 
7649   // get the new Points
7650   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7651     PetscInt b    = points[2 * p];
7652     PetscInt bDof = 0, bSecDof = 0, bOff;
7653 
7654     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7655     if (!bSecDof) continue;
7656     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7657     if (bDof) {
7658       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7659       for (PetscInt q = 0; q < bDof; q++) {
7660         PetscInt a = anchors[bOff + q], aDof = 0;
7661 
7662         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7663         if (aDof) {
7664           newPoints[2 * newP]     = a;
7665           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7666           newP++;
7667         }
7668       }
7669     } else {
7670       newPoints[2 * newP]     = b;
7671       newPoints[2 * newP + 1] = points[2 * p + 1];
7672       newP++;
7673     }
7674   }
7675 
7676   if (outMat) {
7677     PetscScalar *tmpMat;
7678     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7679     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7680 
7681     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7682     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7683     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7684     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7685 
7686     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7687     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7688 
7689     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7690     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7691 
7692     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7693     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7694     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7695     // for each field, insert the anchor modification into modMat
7696     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7697       PetscInt fStart    = oldOffsets[f];
7698       PetscInt fNewStart = newOffsets[f];
7699       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7700         PetscInt b    = points[2 * p];
7701         PetscInt bDof = 0, bSecDof = 0, bOff;
7702 
7703         if (b >= sStart && b < sEnd) {
7704           if (numFields) {
7705             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7706           } else {
7707             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7708           }
7709         }
7710         if (!bSecDof) continue;
7711         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7712         if (bDof) {
7713           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7714           for (PetscInt q = 0; q < bDof; q++, newP++) {
7715             PetscInt a = anchors[bOff + q], aDof = 0;
7716 
7717             if (a >= sStart && a < sEnd) {
7718               if (numFields) {
7719                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7720               } else {
7721                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7722               }
7723             }
7724             if (aDof) {
7725               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7726               for (PetscInt d = 0; d < bSecDof; d++) {
7727                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7728               }
7729             }
7730             oNew += aDof;
7731           }
7732         } else {
7733           // Insert the identity matrix in this block
7734           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7735           oNew += bSecDof;
7736           newP++;
7737         }
7738         o += bSecDof;
7739       }
7740     }
7741 
7742     *outMat = modMat;
7743 
7744     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7745     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7746     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7747     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7748     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7749   }
7750   PetscCall(ISRestoreIndices(aIS, &anchors));
7751 
7752   /* output */
7753   if (outPoints) {
7754     *outPoints = newPoints;
7755   } else {
7756     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7757   }
7758   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7759   PetscFunctionReturn(PETSC_SUCCESS);
7760 }
7761 
7762 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat_Internal(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt numRows, PetscInt numCols, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyRight, PetscBool multiplyLeft)
7763 {
7764   PetscScalar *modMat        = NULL;
7765   PetscInt     newNumIndices = -1;
7766 
7767   PetscFunctionBegin;
7768   /* If M is the matrix represented by values, get the matrix C such that we will add M * C (or, if multiplyLeft, C^T * M * C) into the global matrix.
7769      modMat is that matrix C */
7770   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7771   if (outNumIndices) *outNumIndices = newNumIndices;
7772   if (modMat) {
7773     const PetscScalar *newValues = values;
7774 
7775     if (multiplyRight) {
7776       PetscScalar *newNewValues = NULL;
7777       PetscBLASInt M            = newNumIndices;
7778       PetscBLASInt N            = numRows;
7779       PetscBLASInt K            = numIndices;
7780       PetscScalar  a = 1.0, b = 0.0;
7781 
7782       PetscCheck(numCols == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of columns: %" PetscInt_FMT ", expected %" PetscInt_FMT, numCols, numIndices);
7783 
7784       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7785       // row-major to column-major conversion, right multiplication becomes left multiplication
7786       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7787 
7788       numCols   = newNumIndices;
7789       newValues = newNewValues;
7790     }
7791 
7792     if (multiplyLeft) {
7793       PetscScalar *newNewValues = NULL;
7794       PetscBLASInt M            = numCols;
7795       PetscBLASInt N            = newNumIndices;
7796       PetscBLASInt K            = numIndices;
7797       PetscScalar  a = 1.0, b = 0.0;
7798 
7799       PetscCheck(numRows == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of rows: %" PetscInt_FMT ", expected %" PetscInt_FMT, numRows, numIndices);
7800 
7801       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7802       // row-major to column-major conversion, left multiplication becomes right multiplication
7803       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7804       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7805       newValues = newNewValues;
7806     }
7807     *outValues = (PetscScalar *)newValues;
7808     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7809   }
7810   PetscFunctionReturn(PETSC_SUCCESS);
7811 }
7812 
7813 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyLeft)
7814 {
7815   PetscFunctionBegin;
7816   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7817   PetscFunctionReturn(PETSC_SUCCESS);
7818 }
7819 
7820 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7821 {
7822   /* Closure ordering */
7823   PetscSection    clSection;
7824   IS              clPoints;
7825   const PetscInt *clp;
7826   PetscInt       *points;
7827   PetscInt        Ncl, Ni = 0;
7828 
7829   PetscFunctionBeginHot;
7830   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7831   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7832     PetscInt dof;
7833 
7834     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7835     Ni += dof;
7836   }
7837   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7838   *closureSize = Ni;
7839   PetscFunctionReturn(PETSC_SUCCESS);
7840 }
7841 
7842 static PetscErrorCode DMPlexGetClosureIndices_Internal(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numRows, PetscInt *numCols, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[], PetscBool multiplyRight, PetscBool multiplyLeft)
7843 {
7844   /* Closure ordering */
7845   PetscSection    clSection;
7846   IS              clPoints;
7847   const PetscInt *clp;
7848   PetscInt       *points;
7849   const PetscInt *clperm = NULL;
7850   /* Dof permutation and sign flips */
7851   const PetscInt    **perms[32] = {NULL};
7852   const PetscScalar **flips[32] = {NULL};
7853   PetscScalar        *valCopy   = NULL;
7854   /* Hanging node constraints */
7855   PetscInt    *pointsC = NULL;
7856   PetscScalar *valuesC = NULL;
7857   PetscInt     NclC, NiC;
7858 
7859   PetscInt *idx;
7860   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7861   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7862   PetscInt  idxStart, idxEnd;
7863   PetscInt  nRows, nCols;
7864 
7865   PetscFunctionBeginHot;
7866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7867   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7868   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7869   PetscAssertPointer(numRows, 6);
7870   PetscAssertPointer(numCols, 7);
7871   if (indices) PetscAssertPointer(indices, 8);
7872   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7873   if (values) PetscAssertPointer(values, 10);
7874   PetscCall(PetscSectionGetNumFields(section, &Nf));
7875   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7876   PetscCall(PetscArrayzero(offsets, 32));
7877   /* 1) Get points in closure */
7878   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7879   if (useClPerm) {
7880     PetscInt depth, clsize;
7881     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7882     for (clsize = 0, p = 0; p < Ncl; p++) {
7883       PetscInt dof;
7884       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7885       clsize += dof;
7886     }
7887     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7888   }
7889   /* 2) Get number of indices on these points and field offsets from section */
7890   for (p = 0; p < Ncl * 2; p += 2) {
7891     PetscInt dof, fdof;
7892 
7893     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7894     for (f = 0; f < Nf; ++f) {
7895       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7896       offsets[f + 1] += fdof;
7897     }
7898     Ni += dof;
7899   }
7900   if (*numRows == -1) *numRows = Ni;
7901   if (*numCols == -1) *numCols = Ni;
7902   nRows = *numRows;
7903   nCols = *numCols;
7904   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7905   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7906   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7907   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7908   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7909   for (f = 0; f < PetscMax(1, Nf); ++f) {
7910     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7911     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7912     /* may need to apply sign changes to the element matrix */
7913     if (values && flips[f]) {
7914       PetscInt foffset = offsets[f];
7915 
7916       for (p = 0; p < Ncl; ++p) {
7917         PetscInt           pnt  = points[2 * p], fdof;
7918         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7919 
7920         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7921         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7922         if (flip) {
7923           PetscInt i, j, k;
7924 
7925           if (!valCopy) {
7926             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7927             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7928             *values = valCopy;
7929           }
7930           for (i = 0; i < fdof; ++i) {
7931             PetscScalar fval = flip[i];
7932 
7933             if (multiplyRight) {
7934               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
7935             }
7936             if (multiplyLeft) {
7937               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
7938             }
7939           }
7940         }
7941         foffset += fdof;
7942       }
7943     }
7944   }
7945   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7946   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
7947   if (NclC) {
7948     if (multiplyRight) { *numCols = nCols = NiC; }
7949     if (multiplyLeft) { *numRows = nRows = NiC; }
7950     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7951     for (f = 0; f < PetscMax(1, Nf); ++f) {
7952       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7953       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7954     }
7955     for (f = 0; f < PetscMax(1, Nf); ++f) {
7956       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7957       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7958     }
7959     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7960     Ncl    = NclC;
7961     Ni     = NiC;
7962     points = pointsC;
7963     if (values) *values = valuesC;
7964   }
7965   /* 5) Calculate indices */
7966   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7967   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
7968   if (Nf) {
7969     PetscInt  idxOff;
7970     PetscBool useFieldOffsets;
7971 
7972     if (outOffsets) {
7973       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7974     }
7975     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7976     if (useFieldOffsets) {
7977       for (p = 0; p < Ncl; ++p) {
7978         const PetscInt pnt = points[p * 2];
7979 
7980         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7981       }
7982     } else {
7983       for (p = 0; p < Ncl; ++p) {
7984         const PetscInt pnt = points[p * 2];
7985 
7986         if (pnt < idxStart || pnt >= idxEnd) continue;
7987         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7988         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7989          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7990          * global section. */
7991         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7992       }
7993     }
7994   } else {
7995     PetscInt off = 0, idxOff;
7996 
7997     for (p = 0; p < Ncl; ++p) {
7998       const PetscInt  pnt  = points[p * 2];
7999       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8000 
8001       if (pnt < idxStart || pnt >= idxEnd) continue;
8002       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8003       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8004        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8005       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8006     }
8007   }
8008   /* 6) Cleanup */
8009   for (f = 0; f < PetscMax(1, Nf); ++f) {
8010     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8011     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8012   }
8013   if (NclC) {
8014     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8015   } else {
8016     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8017   }
8018 
8019   if (indices) *indices = idx;
8020   PetscFunctionReturn(PETSC_SUCCESS);
8021 }
8022 
8023 /*@C
8024   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8025 
8026   Not collective
8027 
8028   Input Parameters:
8029 + dm         - The `DM`
8030 . section    - The `PetscSection` describing the points (a local section)
8031 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8032 . point      - The point defining the closure
8033 - useClPerm  - Use the closure point permutation if available
8034 
8035   Output Parameters:
8036 + numIndices - The number of dof indices in the closure of point with the input sections
8037 . indices    - The dof indices
8038 . outOffsets - Array to write the field offsets into, or `NULL`
8039 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8040 
8041   Level: advanced
8042 
8043   Notes:
8044   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
8045 
8046   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8047   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8048   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8049   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8050   indices (with the above semantics) are implied.
8051 
8052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8053           `PetscSection`, `DMGetGlobalSection()`
8054 @*/
8055 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8056 {
8057   PetscInt numRows = -1, numCols = -1;
8058 
8059   PetscFunctionBeginHot;
8060   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8061   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8062   *numIndices = numRows;
8063   PetscFunctionReturn(PETSC_SUCCESS);
8064 }
8065 
8066 /*@C
8067   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8068 
8069   Not collective
8070 
8071   Input Parameters:
8072 + dm         - The `DM`
8073 . section    - The `PetscSection` describing the points (a local section)
8074 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8075 . point      - The point defining the closure
8076 - useClPerm  - Use the closure point permutation if available
8077 
8078   Output Parameters:
8079 + numIndices - The number of dof indices in the closure of point with the input sections
8080 . indices    - The dof indices
8081 . outOffsets - Array to write the field offsets into, or `NULL`
8082 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8083 
8084   Level: advanced
8085 
8086   Notes:
8087   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8088 
8089   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8090   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8091   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8092   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8093   indices (with the above semantics) are implied.
8094 
8095 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8096 @*/
8097 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8098 {
8099   PetscFunctionBegin;
8100   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8101   PetscAssertPointer(indices, 7);
8102   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8103   PetscFunctionReturn(PETSC_SUCCESS);
8104 }
8105 
8106 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8107 {
8108   DM_Plex           *mesh = (DM_Plex *)dm->data;
8109   PetscInt          *indices;
8110   PetscInt           numIndices;
8111   const PetscScalar *valuesOrig = values;
8112   PetscErrorCode     ierr;
8113 
8114   PetscFunctionBegin;
8115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8116   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8117   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8118   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8119   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8120   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8121 
8122   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8123 
8124   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8125   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8126   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8127   if (ierr) {
8128     PetscMPIInt rank;
8129 
8130     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8131     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8132     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8133     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8134     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8135     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8136   }
8137   if (mesh->printFEM > 1) {
8138     PetscInt i;
8139     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8140     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8141     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8142   }
8143 
8144   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8145   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8146   PetscFunctionReturn(PETSC_SUCCESS);
8147 }
8148 
8149 /*@C
8150   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8151 
8152   Not collective
8153 
8154   Input Parameters:
8155 + dm            - The `DM`
8156 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8157 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8158 . A             - The matrix
8159 . point         - The point in the `DM`
8160 . values        - The array of values
8161 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8162 
8163   Level: intermediate
8164 
8165 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8166 @*/
8167 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8168 {
8169   PetscFunctionBegin;
8170   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8171   PetscFunctionReturn(PETSC_SUCCESS);
8172 }
8173 
8174 /*@C
8175   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8176 
8177   Not collective
8178 
8179   Input Parameters:
8180 + dmRow            - The `DM` for the row fields
8181 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8182 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8183 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8184 . dmCol            - The `DM` for the column fields
8185 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8186 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8187 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8188 . A                - The matrix
8189 . point            - The point in the `DM`
8190 . values           - The array of values
8191 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8192 
8193   Level: intermediate
8194 
8195 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8196 @*/
8197 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)
8198 {
8199   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8200   PetscInt          *indicesRow, *indicesCol;
8201   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8202   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8203 
8204   PetscErrorCode ierr;
8205 
8206   PetscFunctionBegin;
8207   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8208   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8209   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8210   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8211   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8212   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8213   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8214   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8215   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8216   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8217   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8218 
8219   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8220   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8221   valuesV1 = valuesV0;
8222   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8223   valuesV2 = valuesV1;
8224   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8225 
8226   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8227   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8228   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8229   if (ierr) {
8230     PetscMPIInt rank;
8231 
8232     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8233     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8234     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8235     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8236     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8237     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8238     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8239   }
8240 
8241   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8242   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8243   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8244   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8245   PetscFunctionReturn(PETSC_SUCCESS);
8246 }
8247 
8248 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8249 {
8250   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8251   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8252   PetscInt       *cpoints = NULL;
8253   PetscInt       *findices, *cindices;
8254   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8255   PetscInt        foffsets[32], coffsets[32];
8256   DMPolytopeType  ct;
8257   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8258   PetscErrorCode  ierr;
8259 
8260   PetscFunctionBegin;
8261   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8262   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8263   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8264   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8265   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8266   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8267   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8268   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8269   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8270   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8271   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8272   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8273   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8274   PetscCall(PetscArrayzero(foffsets, 32));
8275   PetscCall(PetscArrayzero(coffsets, 32));
8276   /* Column indices */
8277   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8278   maxFPoints = numCPoints;
8279   /* Compress out points not in the section */
8280   /*   TODO: Squeeze out points with 0 dof as well */
8281   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8282   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8283     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8284       cpoints[q * 2]     = cpoints[p];
8285       cpoints[q * 2 + 1] = cpoints[p + 1];
8286       ++q;
8287     }
8288   }
8289   numCPoints = q;
8290   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8291     PetscInt fdof;
8292 
8293     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8294     if (!dof) continue;
8295     for (f = 0; f < numFields; ++f) {
8296       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8297       coffsets[f + 1] += fdof;
8298     }
8299     numCIndices += dof;
8300   }
8301   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8302   /* Row indices */
8303   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8304   {
8305     DMPlexTransform tr;
8306     DMPolytopeType *rct;
8307     PetscInt       *rsize, *rcone, *rornt, Nt;
8308 
8309     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8310     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8311     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8312     numSubcells = rsize[Nt - 1];
8313     PetscCall(DMPlexTransformDestroy(&tr));
8314   }
8315   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8316   for (r = 0, q = 0; r < numSubcells; ++r) {
8317     /* TODO Map from coarse to fine cells */
8318     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8319     /* Compress out points not in the section */
8320     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8321     for (p = 0; p < numFPoints * 2; p += 2) {
8322       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8323         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8324         if (!dof) continue;
8325         for (s = 0; s < q; ++s)
8326           if (fpoints[p] == ftotpoints[s * 2]) break;
8327         if (s < q) continue;
8328         ftotpoints[q * 2]     = fpoints[p];
8329         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8330         ++q;
8331       }
8332     }
8333     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8334   }
8335   numFPoints = q;
8336   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8337     PetscInt fdof;
8338 
8339     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8340     if (!dof) continue;
8341     for (f = 0; f < numFields; ++f) {
8342       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8343       foffsets[f + 1] += fdof;
8344     }
8345     numFIndices += dof;
8346   }
8347   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8348 
8349   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8350   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8351   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8352   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8353   if (numFields) {
8354     const PetscInt **permsF[32] = {NULL};
8355     const PetscInt **permsC[32] = {NULL};
8356 
8357     for (f = 0; f < numFields; f++) {
8358       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8359       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8360     }
8361     for (p = 0; p < numFPoints; p++) {
8362       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8363       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8364     }
8365     for (p = 0; p < numCPoints; p++) {
8366       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8367       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8368     }
8369     for (f = 0; f < numFields; f++) {
8370       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8371       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8372     }
8373   } else {
8374     const PetscInt **permsF = NULL;
8375     const PetscInt **permsC = NULL;
8376 
8377     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8378     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8379     for (p = 0, off = 0; p < numFPoints; p++) {
8380       const PetscInt *perm = permsF ? permsF[p] : NULL;
8381 
8382       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8383       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8384     }
8385     for (p = 0, off = 0; p < numCPoints; p++) {
8386       const PetscInt *perm = permsC ? permsC[p] : NULL;
8387 
8388       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8389       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8390     }
8391     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8392     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8393   }
8394   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8395   /* TODO: flips */
8396   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8397   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8398   if (ierr) {
8399     PetscMPIInt rank;
8400 
8401     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8402     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8403     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8404     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8405     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8406   }
8407   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8408   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8409   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8410   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8411   PetscFunctionReturn(PETSC_SUCCESS);
8412 }
8413 
8414 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8415 {
8416   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8417   PetscInt       *cpoints      = NULL;
8418   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8419   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8420   DMPolytopeType  ct;
8421   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8422 
8423   PetscFunctionBegin;
8424   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8425   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8426   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8427   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8428   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8429   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8430   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8431   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8432   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8433   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8434   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8435   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8436   /* Column indices */
8437   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8438   maxFPoints = numCPoints;
8439   /* Compress out points not in the section */
8440   /*   TODO: Squeeze out points with 0 dof as well */
8441   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8442   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8443     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8444       cpoints[q * 2]     = cpoints[p];
8445       cpoints[q * 2 + 1] = cpoints[p + 1];
8446       ++q;
8447     }
8448   }
8449   numCPoints = q;
8450   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8451     PetscInt fdof;
8452 
8453     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8454     if (!dof) continue;
8455     for (f = 0; f < numFields; ++f) {
8456       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8457       coffsets[f + 1] += fdof;
8458     }
8459     numCIndices += dof;
8460   }
8461   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8462   /* Row indices */
8463   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8464   {
8465     DMPlexTransform tr;
8466     DMPolytopeType *rct;
8467     PetscInt       *rsize, *rcone, *rornt, Nt;
8468 
8469     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8470     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8471     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8472     numSubcells = rsize[Nt - 1];
8473     PetscCall(DMPlexTransformDestroy(&tr));
8474   }
8475   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8476   for (r = 0, q = 0; r < numSubcells; ++r) {
8477     /* TODO Map from coarse to fine cells */
8478     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8479     /* Compress out points not in the section */
8480     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8481     for (p = 0; p < numFPoints * 2; p += 2) {
8482       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8483         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8484         if (!dof) continue;
8485         for (s = 0; s < q; ++s)
8486           if (fpoints[p] == ftotpoints[s * 2]) break;
8487         if (s < q) continue;
8488         ftotpoints[q * 2]     = fpoints[p];
8489         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8490         ++q;
8491       }
8492     }
8493     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8494   }
8495   numFPoints = q;
8496   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8497     PetscInt fdof;
8498 
8499     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8500     if (!dof) continue;
8501     for (f = 0; f < numFields; ++f) {
8502       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8503       foffsets[f + 1] += fdof;
8504     }
8505     numFIndices += dof;
8506   }
8507   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8508 
8509   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8510   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8511   if (numFields) {
8512     const PetscInt **permsF[32] = {NULL};
8513     const PetscInt **permsC[32] = {NULL};
8514 
8515     for (f = 0; f < numFields; f++) {
8516       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8517       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8518     }
8519     for (p = 0; p < numFPoints; p++) {
8520       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8521       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8522     }
8523     for (p = 0; p < numCPoints; p++) {
8524       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8525       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8526     }
8527     for (f = 0; f < numFields; f++) {
8528       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8529       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8530     }
8531   } else {
8532     const PetscInt **permsF = NULL;
8533     const PetscInt **permsC = NULL;
8534 
8535     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8536     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8537     for (p = 0, off = 0; p < numFPoints; p++) {
8538       const PetscInt *perm = permsF ? permsF[p] : NULL;
8539 
8540       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8541       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8542     }
8543     for (p = 0, off = 0; p < numCPoints; p++) {
8544       const PetscInt *perm = permsC ? permsC[p] : NULL;
8545 
8546       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8547       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8548     }
8549     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8550     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8551   }
8552   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8553   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8554   PetscFunctionReturn(PETSC_SUCCESS);
8555 }
8556 
8557 /*@C
8558   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8559 
8560   Input Parameter:
8561 . dm - The `DMPLEX` object
8562 
8563   Output Parameter:
8564 . cellHeight - The height of a cell
8565 
8566   Level: developer
8567 
8568 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8569 @*/
8570 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8571 {
8572   DM_Plex *mesh = (DM_Plex *)dm->data;
8573 
8574   PetscFunctionBegin;
8575   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8576   PetscAssertPointer(cellHeight, 2);
8577   *cellHeight = mesh->vtkCellHeight;
8578   PetscFunctionReturn(PETSC_SUCCESS);
8579 }
8580 
8581 /*@C
8582   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8583 
8584   Input Parameters:
8585 + dm         - The `DMPLEX` object
8586 - cellHeight - The height of a cell
8587 
8588   Level: developer
8589 
8590 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8591 @*/
8592 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8593 {
8594   DM_Plex *mesh = (DM_Plex *)dm->data;
8595 
8596   PetscFunctionBegin;
8597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8598   mesh->vtkCellHeight = cellHeight;
8599   PetscFunctionReturn(PETSC_SUCCESS);
8600 }
8601 
8602 /*@
8603   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8604 
8605   Input Parameters:
8606 + dm - The `DMPLEX` object
8607 - ct - The `DMPolytopeType` of the cell
8608 
8609   Output Parameters:
8610 + start - The first cell of this type, or `NULL`
8611 - end   - The upper bound on this celltype, or `NULL`
8612 
8613   Level: advanced
8614 
8615 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8616 @*/
8617 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8618 {
8619   DM_Plex *mesh = (DM_Plex *)dm->data;
8620   DMLabel  label;
8621   PetscInt pStart, pEnd;
8622 
8623   PetscFunctionBegin;
8624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8625   if (start) {
8626     PetscAssertPointer(start, 3);
8627     *start = 0;
8628   }
8629   if (end) {
8630     PetscAssertPointer(end, 4);
8631     *end = 0;
8632   }
8633   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8634   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8635   if (mesh->tr) {
8636     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8637   } else {
8638     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8639     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8640     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8641   }
8642   PetscFunctionReturn(PETSC_SUCCESS);
8643 }
8644 
8645 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8646 {
8647   PetscSection section, globalSection;
8648   PetscInt    *numbers, p;
8649 
8650   PetscFunctionBegin;
8651   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8652   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8653   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8654   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8655   PetscCall(PetscSectionSetUp(section));
8656   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8657   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8658   for (p = pStart; p < pEnd; ++p) {
8659     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8660     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8661     else numbers[p - pStart] += shift;
8662   }
8663   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8664   if (globalSize) {
8665     PetscLayout layout;
8666     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8667     PetscCall(PetscLayoutGetSize(layout, globalSize));
8668     PetscCall(PetscLayoutDestroy(&layout));
8669   }
8670   PetscCall(PetscSectionDestroy(&section));
8671   PetscCall(PetscSectionDestroy(&globalSection));
8672   PetscFunctionReturn(PETSC_SUCCESS);
8673 }
8674 
8675 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8676 {
8677   PetscInt cellHeight, cStart, cEnd;
8678 
8679   PetscFunctionBegin;
8680   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8681   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8682   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8683   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8684   PetscFunctionReturn(PETSC_SUCCESS);
8685 }
8686 
8687 /*@
8688   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8689 
8690   Input Parameter:
8691 . dm - The `DMPLEX` object
8692 
8693   Output Parameter:
8694 . globalCellNumbers - Global cell numbers for all cells on this process
8695 
8696   Level: developer
8697 
8698 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8699 @*/
8700 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8701 {
8702   DM_Plex *mesh = (DM_Plex *)dm->data;
8703 
8704   PetscFunctionBegin;
8705   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8706   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8707   *globalCellNumbers = mesh->globalCellNumbers;
8708   PetscFunctionReturn(PETSC_SUCCESS);
8709 }
8710 
8711 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8712 {
8713   PetscInt vStart, vEnd;
8714 
8715   PetscFunctionBegin;
8716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8717   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8718   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8719   PetscFunctionReturn(PETSC_SUCCESS);
8720 }
8721 
8722 /*@
8723   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8724 
8725   Input Parameter:
8726 . dm - The `DMPLEX` object
8727 
8728   Output Parameter:
8729 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8730 
8731   Level: developer
8732 
8733 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8734 @*/
8735 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8736 {
8737   DM_Plex *mesh = (DM_Plex *)dm->data;
8738 
8739   PetscFunctionBegin;
8740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8741   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8742   *globalVertexNumbers = mesh->globalVertexNumbers;
8743   PetscFunctionReturn(PETSC_SUCCESS);
8744 }
8745 
8746 /*@
8747   DMPlexCreatePointNumbering - Create a global numbering for all points.
8748 
8749   Collective
8750 
8751   Input Parameter:
8752 . dm - The `DMPLEX` object
8753 
8754   Output Parameter:
8755 . globalPointNumbers - Global numbers for all points on this process
8756 
8757   Level: developer
8758 
8759   Notes:
8760   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8761   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8762   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8763   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8764 
8765   The partitioned mesh is
8766   ```
8767   (2)--0--(3)--1--(4)    (1)--0--(2)
8768   ```
8769   and its global numbering is
8770   ```
8771   (3)--0--(4)--1--(5)--2--(6)
8772   ```
8773   Then the global numbering is provided as
8774   ```
8775   [0] Number of indices in set 5
8776   [0] 0 0
8777   [0] 1 1
8778   [0] 2 3
8779   [0] 3 4
8780   [0] 4 -6
8781   [1] Number of indices in set 3
8782   [1] 0 2
8783   [1] 1 5
8784   [1] 2 6
8785   ```
8786 
8787 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8788 @*/
8789 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8790 {
8791   IS        nums[4];
8792   PetscInt  depths[4], gdepths[4], starts[4];
8793   PetscInt  depth, d, shift = 0;
8794   PetscBool empty = PETSC_FALSE;
8795 
8796   PetscFunctionBegin;
8797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8798   PetscCall(DMPlexGetDepth(dm, &depth));
8799   // For unstratified meshes use dim instead of depth
8800   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8801   // If any stratum is empty, we must mark all empty
8802   for (d = 0; d <= depth; ++d) {
8803     PetscInt end;
8804 
8805     depths[d] = depth - d;
8806     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8807     if (!(starts[d] - end)) empty = PETSC_TRUE;
8808   }
8809   if (empty)
8810     for (d = 0; d <= depth; ++d) {
8811       depths[d] = -1;
8812       starts[d] = -1;
8813     }
8814   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8815   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8816   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]);
8817   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8818   for (d = 0; d <= depth; ++d) {
8819     PetscInt pStart, pEnd, gsize;
8820 
8821     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8822     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8823     shift += gsize;
8824   }
8825   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8826   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8827   PetscFunctionReturn(PETSC_SUCCESS);
8828 }
8829 
8830 /*@
8831   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8832 
8833   Input Parameter:
8834 . dm - The `DMPLEX` object
8835 
8836   Output Parameter:
8837 . ranks - The rank field
8838 
8839   Options Database Key:
8840 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8841 
8842   Level: intermediate
8843 
8844 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8845 @*/
8846 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8847 {
8848   DM             rdm;
8849   PetscFE        fe;
8850   PetscScalar   *r;
8851   PetscMPIInt    rank;
8852   DMPolytopeType ct;
8853   PetscInt       dim, cStart, cEnd, c;
8854   PetscBool      simplex;
8855 
8856   PetscFunctionBeginUser;
8857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8858   PetscAssertPointer(ranks, 2);
8859   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8860   PetscCall(DMClone(dm, &rdm));
8861   PetscCall(DMGetDimension(rdm, &dim));
8862   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8863   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8864   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8865   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8866   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8867   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8868   PetscCall(PetscFEDestroy(&fe));
8869   PetscCall(DMCreateDS(rdm));
8870   PetscCall(DMCreateGlobalVector(rdm, ranks));
8871   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8872   PetscCall(VecGetArray(*ranks, &r));
8873   for (c = cStart; c < cEnd; ++c) {
8874     PetscScalar *lr;
8875 
8876     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8877     if (lr) *lr = rank;
8878   }
8879   PetscCall(VecRestoreArray(*ranks, &r));
8880   PetscCall(DMDestroy(&rdm));
8881   PetscFunctionReturn(PETSC_SUCCESS);
8882 }
8883 
8884 /*@
8885   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8886 
8887   Input Parameters:
8888 + dm    - The `DMPLEX`
8889 - label - The `DMLabel`
8890 
8891   Output Parameter:
8892 . val - The label value field
8893 
8894   Options Database Key:
8895 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8896 
8897   Level: intermediate
8898 
8899 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8900 @*/
8901 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8902 {
8903   DM             rdm, plex;
8904   Vec            lval;
8905   PetscSection   section;
8906   PetscFE        fe;
8907   PetscScalar   *v;
8908   PetscInt       dim, pStart, pEnd, p, cStart;
8909   DMPolytopeType ct;
8910   char           name[PETSC_MAX_PATH_LEN];
8911   const char    *lname, *prefix;
8912 
8913   PetscFunctionBeginUser;
8914   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8915   PetscAssertPointer(label, 2);
8916   PetscAssertPointer(val, 3);
8917   PetscCall(DMClone(dm, &rdm));
8918   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8919   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8920   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8921   PetscCall(DMDestroy(&plex));
8922   PetscCall(DMGetDimension(rdm, &dim));
8923   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8924   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8925   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8926   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8927   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8928   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8929   PetscCall(PetscFEDestroy(&fe));
8930   PetscCall(DMCreateDS(rdm));
8931   PetscCall(DMCreateGlobalVector(rdm, val));
8932   PetscCall(DMCreateLocalVector(rdm, &lval));
8933   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8934   PetscCall(DMGetLocalSection(rdm, &section));
8935   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8936   PetscCall(VecGetArray(lval, &v));
8937   for (p = pStart; p < pEnd; ++p) {
8938     PetscInt cval, dof, off;
8939 
8940     PetscCall(PetscSectionGetDof(section, p, &dof));
8941     if (!dof) continue;
8942     PetscCall(DMLabelGetValue(label, p, &cval));
8943     PetscCall(PetscSectionGetOffset(section, p, &off));
8944     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8945   }
8946   PetscCall(VecRestoreArray(lval, &v));
8947   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8948   PetscCall(VecDestroy(&lval));
8949   PetscCall(DMDestroy(&rdm));
8950   PetscFunctionReturn(PETSC_SUCCESS);
8951 }
8952 
8953 /*@
8954   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8955 
8956   Input Parameter:
8957 . dm - The `DMPLEX` object
8958 
8959   Level: developer
8960 
8961   Notes:
8962   This is a useful diagnostic when creating meshes programmatically.
8963 
8964   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8965 
8966 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8967 @*/
8968 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8969 {
8970   PetscSection    coneSection, supportSection;
8971   const PetscInt *cone, *support;
8972   PetscInt        coneSize, c, supportSize, s;
8973   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8974   PetscBool       storagecheck = PETSC_TRUE;
8975 
8976   PetscFunctionBegin;
8977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8978   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8979   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8980   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8981   /* Check that point p is found in the support of its cone points, and vice versa */
8982   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8983   for (p = pStart; p < pEnd; ++p) {
8984     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8985     PetscCall(DMPlexGetCone(dm, p, &cone));
8986     for (c = 0; c < coneSize; ++c) {
8987       PetscBool dup = PETSC_FALSE;
8988       PetscInt  d;
8989       for (d = c - 1; d >= 0; --d) {
8990         if (cone[c] == cone[d]) {
8991           dup = PETSC_TRUE;
8992           break;
8993         }
8994       }
8995       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8996       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8997       for (s = 0; s < supportSize; ++s) {
8998         if (support[s] == p) break;
8999       }
9000       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9001         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9002         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9003         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9004         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9005         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9006         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9007         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]);
9008         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9009       }
9010     }
9011     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9012     if (p != pp) {
9013       storagecheck = PETSC_FALSE;
9014       continue;
9015     }
9016     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9017     PetscCall(DMPlexGetSupport(dm, p, &support));
9018     for (s = 0; s < supportSize; ++s) {
9019       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9020       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9021       for (c = 0; c < coneSize; ++c) {
9022         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9023         if (cone[c] != pp) {
9024           c = 0;
9025           break;
9026         }
9027         if (cone[c] == p) break;
9028       }
9029       if (c >= coneSize) {
9030         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9031         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9032         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9033         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9034         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9035         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9036         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9037       }
9038     }
9039   }
9040   if (storagecheck) {
9041     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9042     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9043     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9044   }
9045   PetscFunctionReturn(PETSC_SUCCESS);
9046 }
9047 
9048 /*
9049   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.
9050 */
9051 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9052 {
9053   DMPolytopeType  cct;
9054   PetscInt        ptpoints[4];
9055   const PetscInt *cone, *ccone, *ptcone;
9056   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9057 
9058   PetscFunctionBegin;
9059   *unsplit = 0;
9060   switch (ct) {
9061   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9062     ptpoints[npt++] = c;
9063     break;
9064   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9065     PetscCall(DMPlexGetCone(dm, c, &cone));
9066     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9067     for (cp = 0; cp < coneSize; ++cp) {
9068       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9069       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9070     }
9071     break;
9072   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9073   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9074     PetscCall(DMPlexGetCone(dm, c, &cone));
9075     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9076     for (cp = 0; cp < coneSize; ++cp) {
9077       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9078       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9079       for (ccp = 0; ccp < cconeSize; ++ccp) {
9080         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9081         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9082           PetscInt p;
9083           for (p = 0; p < npt; ++p)
9084             if (ptpoints[p] == ccone[ccp]) break;
9085           if (p == npt) ptpoints[npt++] = ccone[ccp];
9086         }
9087       }
9088     }
9089     break;
9090   default:
9091     break;
9092   }
9093   for (pt = 0; pt < npt; ++pt) {
9094     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9095     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9096   }
9097   PetscFunctionReturn(PETSC_SUCCESS);
9098 }
9099 
9100 /*@
9101   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9102 
9103   Input Parameters:
9104 + dm         - The `DMPLEX` object
9105 - cellHeight - Normally 0
9106 
9107   Level: developer
9108 
9109   Notes:
9110   This is a useful diagnostic when creating meshes programmatically.
9111   Currently applicable only to homogeneous simplex or tensor meshes.
9112 
9113   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9114 
9115 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9116 @*/
9117 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9118 {
9119   DMPlexInterpolatedFlag interp;
9120   DMPolytopeType         ct;
9121   PetscInt               vStart, vEnd, cStart, cEnd, c;
9122 
9123   PetscFunctionBegin;
9124   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9125   PetscCall(DMPlexIsInterpolated(dm, &interp));
9126   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9127   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9128   for (c = cStart; c < cEnd; ++c) {
9129     PetscInt *closure = NULL;
9130     PetscInt  coneSize, closureSize, cl, Nv = 0;
9131 
9132     PetscCall(DMPlexGetCellType(dm, c, &ct));
9133     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9134     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9135     if (interp == DMPLEX_INTERPOLATED_FULL) {
9136       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9137       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));
9138     }
9139     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9140     for (cl = 0; cl < closureSize * 2; cl += 2) {
9141       const PetscInt p = closure[cl];
9142       if ((p >= vStart) && (p < vEnd)) ++Nv;
9143     }
9144     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9145     /* Special Case: Tensor faces with identified vertices */
9146     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9147       PetscInt unsplit;
9148 
9149       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9150       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9151     }
9152     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));
9153   }
9154   PetscFunctionReturn(PETSC_SUCCESS);
9155 }
9156 
9157 /*@
9158   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9159 
9160   Collective
9161 
9162   Input Parameters:
9163 + dm         - The `DMPLEX` object
9164 - cellHeight - Normally 0
9165 
9166   Level: developer
9167 
9168   Notes:
9169   This is a useful diagnostic when creating meshes programmatically.
9170   This routine is only relevant for meshes that are fully interpolated across all ranks.
9171   It will error out if a partially interpolated mesh is given on some rank.
9172   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9173 
9174   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9175 
9176 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9177 @*/
9178 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9179 {
9180   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9181   DMPlexInterpolatedFlag interpEnum;
9182 
9183   PetscFunctionBegin;
9184   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9185   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9186   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9187   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9188     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9189     PetscFunctionReturn(PETSC_SUCCESS);
9190   }
9191 
9192   PetscCall(DMGetDimension(dm, &dim));
9193   PetscCall(DMPlexGetDepth(dm, &depth));
9194   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9195   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9196     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9197     for (c = cStart; c < cEnd; ++c) {
9198       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9199       const DMPolytopeType *faceTypes;
9200       DMPolytopeType        ct;
9201       PetscInt              numFaces, coneSize, f;
9202       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9203 
9204       PetscCall(DMPlexGetCellType(dm, c, &ct));
9205       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9206       if (unsplit) continue;
9207       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9208       PetscCall(DMPlexGetCone(dm, c, &cone));
9209       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9210       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9211       for (cl = 0; cl < closureSize * 2; cl += 2) {
9212         const PetscInt p = closure[cl];
9213         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9214       }
9215       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9216       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);
9217       for (f = 0; f < numFaces; ++f) {
9218         DMPolytopeType fct;
9219         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9220 
9221         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9222         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9223         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9224           const PetscInt p = fclosure[cl];
9225           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9226         }
9227         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]);
9228         for (v = 0; v < fnumCorners; ++v) {
9229           if (fclosure[v] != faces[fOff + v]) {
9230             PetscInt v1;
9231 
9232             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9233             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9234             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9235             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9236             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9237             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]);
9238           }
9239         }
9240         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9241         fOff += faceSizes[f];
9242       }
9243       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9244       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9245     }
9246   }
9247   PetscFunctionReturn(PETSC_SUCCESS);
9248 }
9249 
9250 /*@
9251   DMPlexCheckGeometry - Check the geometry of mesh cells
9252 
9253   Input Parameter:
9254 . dm - The `DMPLEX` object
9255 
9256   Level: developer
9257 
9258   Notes:
9259   This is a useful diagnostic when creating meshes programmatically.
9260 
9261   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9262 
9263 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9264 @*/
9265 PetscErrorCode DMPlexCheckGeometry(DM dm)
9266 {
9267   Vec       coordinates;
9268   PetscReal detJ, J[9], refVol = 1.0;
9269   PetscReal vol;
9270   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9271 
9272   PetscFunctionBegin;
9273   PetscCall(DMGetDimension(dm, &dim));
9274   PetscCall(DMGetCoordinateDim(dm, &dE));
9275   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9276   PetscCall(DMPlexGetDepth(dm, &depth));
9277   for (d = 0; d < dim; ++d) refVol *= 2.0;
9278   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9279   /* Make sure local coordinates are created, because that step is collective */
9280   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9281   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9282   for (c = cStart; c < cEnd; ++c) {
9283     DMPolytopeType ct;
9284     PetscInt       unsplit;
9285     PetscBool      ignoreZeroVol = PETSC_FALSE;
9286 
9287     PetscCall(DMPlexGetCellType(dm, c, &ct));
9288     switch (ct) {
9289     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9290     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9291     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9292       ignoreZeroVol = PETSC_TRUE;
9293       break;
9294     default:
9295       break;
9296     }
9297     switch (ct) {
9298     case DM_POLYTOPE_TRI_PRISM:
9299     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9300     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9301     case DM_POLYTOPE_PYRAMID:
9302       continue;
9303     default:
9304       break;
9305     }
9306     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9307     if (unsplit) continue;
9308     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9309     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);
9310     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9311     /* This should work with periodicity since DG coordinates should be used */
9312     if (depth > 1) {
9313       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9314       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);
9315       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9316     }
9317   }
9318   PetscFunctionReturn(PETSC_SUCCESS);
9319 }
9320 
9321 /*@
9322   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9323 
9324   Collective
9325 
9326   Input Parameters:
9327 + dm              - The `DMPLEX` object
9328 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9329 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9330 
9331   Level: developer
9332 
9333   Notes:
9334   This is mainly intended for debugging/testing purposes.
9335 
9336   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9337 
9338   Extra roots can come from periodic cuts, where additional points appear on the boundary
9339 
9340 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9341 @*/
9342 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9343 {
9344   PetscInt           l, nleaves, nroots, overlap;
9345   const PetscInt    *locals;
9346   const PetscSFNode *remotes;
9347   PetscBool          distributed;
9348   MPI_Comm           comm;
9349   PetscMPIInt        rank;
9350 
9351   PetscFunctionBegin;
9352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9353   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9354   else pointSF = dm->sf;
9355   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9356   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9357   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9358   {
9359     PetscMPIInt mpiFlag;
9360 
9361     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9362     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9363   }
9364   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9365   PetscCall(DMPlexIsDistributed(dm, &distributed));
9366   if (!distributed) {
9367     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);
9368     PetscFunctionReturn(PETSC_SUCCESS);
9369   }
9370   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);
9371   PetscCall(DMPlexGetOverlap(dm, &overlap));
9372 
9373   /* Check SF graph is compatible with DMPlex chart */
9374   {
9375     PetscInt pStart, pEnd, maxLeaf;
9376 
9377     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9378     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9379     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9380     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9381   }
9382 
9383   /* Check Point SF has no local points referenced */
9384   for (l = 0; l < nleaves; l++) {
9385     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);
9386   }
9387 
9388   /* Check there are no cells in interface */
9389   if (!overlap) {
9390     PetscInt cellHeight, cStart, cEnd;
9391 
9392     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9393     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9394     for (l = 0; l < nleaves; ++l) {
9395       const PetscInt point = locals ? locals[l] : l;
9396 
9397       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9398     }
9399   }
9400 
9401   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9402   {
9403     const PetscInt *rootdegree;
9404 
9405     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9406     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9407     for (l = 0; l < nleaves; ++l) {
9408       const PetscInt  point = locals ? locals[l] : l;
9409       const PetscInt *cone;
9410       PetscInt        coneSize, c, idx;
9411 
9412       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9413       PetscCall(DMPlexGetCone(dm, point, &cone));
9414       for (c = 0; c < coneSize; ++c) {
9415         if (!rootdegree[cone[c]]) {
9416           if (locals) {
9417             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9418           } else {
9419             idx = (cone[c] < nleaves) ? cone[c] : -1;
9420           }
9421           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9422         }
9423       }
9424     }
9425   }
9426   PetscFunctionReturn(PETSC_SUCCESS);
9427 }
9428 
9429 /*@
9430   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9431 
9432   Input Parameter:
9433 . dm - The `DMPLEX` object
9434 
9435   Level: developer
9436 
9437   Notes:
9438   This is a useful diagnostic when creating meshes programmatically.
9439 
9440   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9441 
9442   Currently does not include `DMPlexCheckCellShape()`.
9443 
9444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9445 @*/
9446 PetscErrorCode DMPlexCheck(DM dm)
9447 {
9448   PetscInt cellHeight;
9449 
9450   PetscFunctionBegin;
9451   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9452   PetscCall(DMPlexCheckSymmetry(dm));
9453   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9454   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9455   PetscCall(DMPlexCheckGeometry(dm));
9456   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9457   PetscCall(DMPlexCheckInterfaceCones(dm));
9458   PetscFunctionReturn(PETSC_SUCCESS);
9459 }
9460 
9461 typedef struct cell_stats {
9462   PetscReal min, max, sum, squaresum;
9463   PetscInt  count;
9464 } cell_stats_t;
9465 
9466 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9467 {
9468   PetscInt i, N = *len;
9469 
9470   for (i = 0; i < N; i++) {
9471     cell_stats_t *A = (cell_stats_t *)a;
9472     cell_stats_t *B = (cell_stats_t *)b;
9473 
9474     B->min = PetscMin(A->min, B->min);
9475     B->max = PetscMax(A->max, B->max);
9476     B->sum += A->sum;
9477     B->squaresum += A->squaresum;
9478     B->count += A->count;
9479   }
9480 }
9481 
9482 /*@
9483   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9484 
9485   Collective
9486 
9487   Input Parameters:
9488 + dm        - The `DMPLEX` object
9489 . output    - If true, statistics will be displayed on `stdout`
9490 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9491 
9492   Level: developer
9493 
9494   Notes:
9495   This is mainly intended for debugging/testing purposes.
9496 
9497   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9498 
9499 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9500 @*/
9501 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9502 {
9503   DM           dmCoarse;
9504   cell_stats_t stats, globalStats;
9505   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9506   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9507   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9508   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9509   PetscMPIInt  rank, size;
9510 
9511   PetscFunctionBegin;
9512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9513   stats.min = PETSC_MAX_REAL;
9514   stats.max = PETSC_MIN_REAL;
9515   stats.sum = stats.squaresum = 0.;
9516   stats.count                 = 0;
9517 
9518   PetscCallMPI(MPI_Comm_size(comm, &size));
9519   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9520   PetscCall(DMGetCoordinateDim(dm, &cdim));
9521   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9522   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9523   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9524   for (c = cStart; c < cEnd; c++) {
9525     PetscInt  i;
9526     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9527 
9528     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9529     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9530     for (i = 0; i < PetscSqr(cdim); ++i) {
9531       frobJ += J[i] * J[i];
9532       frobInvJ += invJ[i] * invJ[i];
9533     }
9534     cond2 = frobJ * frobInvJ;
9535     cond  = PetscSqrtReal(cond2);
9536 
9537     stats.min = PetscMin(stats.min, cond);
9538     stats.max = PetscMax(stats.max, cond);
9539     stats.sum += cond;
9540     stats.squaresum += cond2;
9541     stats.count++;
9542     if (output && cond > limit) {
9543       PetscSection coordSection;
9544       Vec          coordsLocal;
9545       PetscScalar *coords = NULL;
9546       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9547 
9548       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9549       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9550       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9551       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9552       for (i = 0; i < Nv / cdim; ++i) {
9553         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9554         for (d = 0; d < cdim; ++d) {
9555           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9556           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9557         }
9558         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9559       }
9560       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9561       for (cl = 0; cl < clSize * 2; cl += 2) {
9562         const PetscInt edge = closure[cl];
9563 
9564         if ((edge >= eStart) && (edge < eEnd)) {
9565           PetscReal len;
9566 
9567           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9568           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9569         }
9570       }
9571       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9572       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9573     }
9574   }
9575   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9576 
9577   if (size > 1) {
9578     PetscMPIInt  blockLengths[2] = {4, 1};
9579     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9580     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9581     MPI_Op       statReduce;
9582 
9583     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9584     PetscCallMPI(MPI_Type_commit(&statType));
9585     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9586     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9587     PetscCallMPI(MPI_Op_free(&statReduce));
9588     PetscCallMPI(MPI_Type_free(&statType));
9589   } else {
9590     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9591   }
9592   if (rank == 0) {
9593     count = globalStats.count;
9594     min   = globalStats.min;
9595     max   = globalStats.max;
9596     mean  = globalStats.sum / globalStats.count;
9597     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9598   }
9599 
9600   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));
9601   PetscCall(PetscFree2(J, invJ));
9602 
9603   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9604   if (dmCoarse) {
9605     PetscBool isplex;
9606 
9607     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9608     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9609   }
9610   PetscFunctionReturn(PETSC_SUCCESS);
9611 }
9612 
9613 /*@
9614   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9615   orthogonal quality below given tolerance.
9616 
9617   Collective
9618 
9619   Input Parameters:
9620 + dm   - The `DMPLEX` object
9621 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9622 - atol - [0, 1] Absolute tolerance for tagging cells.
9623 
9624   Output Parameters:
9625 + OrthQual      - `Vec` containing orthogonal quality per cell
9626 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9627 
9628   Options Database Keys:
9629 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9630 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9631 
9632   Level: intermediate
9633 
9634   Notes:
9635   Orthogonal quality is given by the following formula\:
9636 
9637   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9638 
9639   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
9640   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9641   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9642   calculating the cosine of the angle between these vectors.
9643 
9644   Orthogonal quality ranges from 1 (best) to 0 (worst).
9645 
9646   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9647   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9648 
9649   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9650 
9651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9652 @*/
9653 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9654 {
9655   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9656   PetscInt              *idx;
9657   PetscScalar           *oqVals;
9658   const PetscScalar     *cellGeomArr, *faceGeomArr;
9659   PetscReal             *ci, *fi, *Ai;
9660   MPI_Comm               comm;
9661   Vec                    cellgeom, facegeom;
9662   DM                     dmFace, dmCell;
9663   IS                     glob;
9664   ISLocalToGlobalMapping ltog;
9665   PetscViewer            vwr;
9666 
9667   PetscFunctionBegin;
9668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9669   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9670   PetscAssertPointer(OrthQual, 4);
9671   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9672   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9673   PetscCall(DMGetDimension(dm, &nc));
9674   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9675   {
9676     DMPlexInterpolatedFlag interpFlag;
9677 
9678     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9679     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9680       PetscMPIInt rank;
9681 
9682       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9683       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9684     }
9685   }
9686   if (OrthQualLabel) {
9687     PetscAssertPointer(OrthQualLabel, 5);
9688     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9689     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9690   } else {
9691     *OrthQualLabel = NULL;
9692   }
9693   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9694   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9695   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9696   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9697   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9698   PetscCall(VecCreate(comm, OrthQual));
9699   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9700   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9701   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9702   PetscCall(VecSetUp(*OrthQual));
9703   PetscCall(ISDestroy(&glob));
9704   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9705   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9706   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9707   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9708   PetscCall(VecGetDM(cellgeom, &dmCell));
9709   PetscCall(VecGetDM(facegeom, &dmFace));
9710   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9711   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9712     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9713     PetscInt         cellarr[2], *adj = NULL;
9714     PetscScalar     *cArr, *fArr;
9715     PetscReal        minvalc = 1.0, minvalf = 1.0;
9716     PetscFVCellGeom *cg;
9717 
9718     idx[cellIter] = cell - cStart;
9719     cellarr[0]    = cell;
9720     /* Make indexing into cellGeom easier */
9721     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9722     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9723     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9724     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9725     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9726       PetscInt         i;
9727       const PetscInt   neigh  = adj[cellneigh];
9728       PetscReal        normci = 0, normfi = 0, normai = 0;
9729       PetscFVCellGeom *cgneigh;
9730       PetscFVFaceGeom *fg;
9731 
9732       /* Don't count ourselves in the neighbor list */
9733       if (neigh == cell) continue;
9734       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9735       cellarr[1] = neigh;
9736       {
9737         PetscInt        numcovpts;
9738         const PetscInt *covpts;
9739 
9740         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9741         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9742         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9743       }
9744 
9745       /* Compute c_i, f_i and their norms */
9746       for (i = 0; i < nc; i++) {
9747         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9748         fi[i] = fg->centroid[i] - cg->centroid[i];
9749         Ai[i] = fg->normal[i];
9750         normci += PetscPowReal(ci[i], 2);
9751         normfi += PetscPowReal(fi[i], 2);
9752         normai += PetscPowReal(Ai[i], 2);
9753       }
9754       normci = PetscSqrtReal(normci);
9755       normfi = PetscSqrtReal(normfi);
9756       normai = PetscSqrtReal(normai);
9757 
9758       /* Normalize and compute for each face-cell-normal pair */
9759       for (i = 0; i < nc; i++) {
9760         ci[i] = ci[i] / normci;
9761         fi[i] = fi[i] / normfi;
9762         Ai[i] = Ai[i] / normai;
9763         /* PetscAbs because I don't know if normals are guaranteed to point out */
9764         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9765         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9766       }
9767       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9768       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9769     }
9770     PetscCall(PetscFree(adj));
9771     PetscCall(PetscFree2(cArr, fArr));
9772     /* Defer to cell if they're equal */
9773     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9774     if (OrthQualLabel) {
9775       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9776     }
9777   }
9778   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9779   PetscCall(VecAssemblyBegin(*OrthQual));
9780   PetscCall(VecAssemblyEnd(*OrthQual));
9781   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9782   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9783   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9784   if (OrthQualLabel) {
9785     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9786   }
9787   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9788   PetscCall(PetscOptionsRestoreViewer(&vwr));
9789   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9790   PetscFunctionReturn(PETSC_SUCCESS);
9791 }
9792 
9793 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9794  * interpolator construction */
9795 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9796 {
9797   PetscSection section, newSection, gsection;
9798   PetscSF      sf;
9799   PetscBool    hasConstraints, ghasConstraints;
9800 
9801   PetscFunctionBegin;
9802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9803   PetscAssertPointer(odm, 2);
9804   PetscCall(DMGetLocalSection(dm, &section));
9805   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9806   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9807   if (!ghasConstraints) {
9808     PetscCall(PetscObjectReference((PetscObject)dm));
9809     *odm = dm;
9810     PetscFunctionReturn(PETSC_SUCCESS);
9811   }
9812   PetscCall(DMClone(dm, odm));
9813   PetscCall(DMCopyFields(dm, *odm));
9814   PetscCall(DMGetLocalSection(*odm, &newSection));
9815   PetscCall(DMGetPointSF(*odm, &sf));
9816   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9817   PetscCall(DMSetGlobalSection(*odm, gsection));
9818   PetscCall(PetscSectionDestroy(&gsection));
9819   PetscFunctionReturn(PETSC_SUCCESS);
9820 }
9821 
9822 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9823 {
9824   DM        dmco, dmfo;
9825   Mat       interpo;
9826   Vec       rscale;
9827   Vec       cglobalo, clocal;
9828   Vec       fglobal, fglobalo, flocal;
9829   PetscBool regular;
9830 
9831   PetscFunctionBegin;
9832   PetscCall(DMGetFullDM(dmc, &dmco));
9833   PetscCall(DMGetFullDM(dmf, &dmfo));
9834   PetscCall(DMSetCoarseDM(dmfo, dmco));
9835   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9836   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9837   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9838   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9839   PetscCall(DMCreateLocalVector(dmc, &clocal));
9840   PetscCall(VecSet(cglobalo, 0.));
9841   PetscCall(VecSet(clocal, 0.));
9842   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9843   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9844   PetscCall(DMCreateLocalVector(dmf, &flocal));
9845   PetscCall(VecSet(fglobal, 0.));
9846   PetscCall(VecSet(fglobalo, 0.));
9847   PetscCall(VecSet(flocal, 0.));
9848   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9849   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9850   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9851   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9852   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9853   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9854   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9855   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9856   *shift = fglobal;
9857   PetscCall(VecDestroy(&flocal));
9858   PetscCall(VecDestroy(&fglobalo));
9859   PetscCall(VecDestroy(&clocal));
9860   PetscCall(VecDestroy(&cglobalo));
9861   PetscCall(VecDestroy(&rscale));
9862   PetscCall(MatDestroy(&interpo));
9863   PetscCall(DMDestroy(&dmfo));
9864   PetscCall(DMDestroy(&dmco));
9865   PetscFunctionReturn(PETSC_SUCCESS);
9866 }
9867 
9868 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9869 {
9870   PetscObject shifto;
9871   Vec         shift;
9872 
9873   PetscFunctionBegin;
9874   if (!interp) {
9875     Vec rscale;
9876 
9877     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9878     PetscCall(VecDestroy(&rscale));
9879   } else {
9880     PetscCall(PetscObjectReference((PetscObject)interp));
9881   }
9882   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9883   if (!shifto) {
9884     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9885     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9886     shifto = (PetscObject)shift;
9887     PetscCall(VecDestroy(&shift));
9888   }
9889   shift = (Vec)shifto;
9890   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9891   PetscCall(VecAXPY(fineSol, 1.0, shift));
9892   PetscCall(MatDestroy(&interp));
9893   PetscFunctionReturn(PETSC_SUCCESS);
9894 }
9895 
9896 /* Pointwise interpolation
9897      Just code FEM for now
9898      u^f = I u^c
9899      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9900      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9901      I_{ij} = psi^f_i phi^c_j
9902 */
9903 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9904 {
9905   PetscSection gsc, gsf;
9906   PetscInt     m, n;
9907   void        *ctx;
9908   DM           cdm;
9909   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9910 
9911   PetscFunctionBegin;
9912   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9913   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9914   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9915   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9916 
9917   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9918   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9919   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9920   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9921   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9922 
9923   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9924   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9925   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9926   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9927   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9928   if (scaling) {
9929     /* Use naive scaling */
9930     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9931   }
9932   PetscFunctionReturn(PETSC_SUCCESS);
9933 }
9934 
9935 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9936 {
9937   VecScatter ctx;
9938 
9939   PetscFunctionBegin;
9940   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9941   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9942   PetscCall(VecScatterDestroy(&ctx));
9943   PetscFunctionReturn(PETSC_SUCCESS);
9944 }
9945 
9946 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[])
9947 {
9948   const PetscInt Nc = uOff[1] - uOff[0];
9949   PetscInt       c;
9950   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9951 }
9952 
9953 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9954 {
9955   DM           dmc;
9956   PetscDS      ds;
9957   Vec          ones, locmass;
9958   IS           cellIS;
9959   PetscFormKey key;
9960   PetscInt     depth;
9961 
9962   PetscFunctionBegin;
9963   PetscCall(DMClone(dm, &dmc));
9964   PetscCall(DMCopyDisc(dm, dmc));
9965   PetscCall(DMGetDS(dmc, &ds));
9966   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9967   PetscCall(DMCreateGlobalVector(dmc, mass));
9968   PetscCall(DMGetLocalVector(dmc, &ones));
9969   PetscCall(DMGetLocalVector(dmc, &locmass));
9970   PetscCall(DMPlexGetDepth(dmc, &depth));
9971   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9972   PetscCall(VecSet(locmass, 0.0));
9973   PetscCall(VecSet(ones, 1.0));
9974   key.label = NULL;
9975   key.value = 0;
9976   key.field = 0;
9977   key.part  = 0;
9978   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9979   PetscCall(ISDestroy(&cellIS));
9980   PetscCall(VecSet(*mass, 0.0));
9981   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9982   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9983   PetscCall(DMRestoreLocalVector(dmc, &ones));
9984   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9985   PetscCall(DMDestroy(&dmc));
9986   PetscFunctionReturn(PETSC_SUCCESS);
9987 }
9988 
9989 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9990 {
9991   PetscSection gsc, gsf;
9992   PetscInt     m, n;
9993   void        *ctx;
9994   DM           cdm;
9995   PetscBool    regular;
9996 
9997   PetscFunctionBegin;
9998   if (dmFine == dmCoarse) {
9999     DM            dmc;
10000     PetscDS       ds;
10001     PetscWeakForm wf;
10002     Vec           u;
10003     IS            cellIS;
10004     PetscFormKey  key;
10005     PetscInt      depth;
10006 
10007     PetscCall(DMClone(dmFine, &dmc));
10008     PetscCall(DMCopyDisc(dmFine, dmc));
10009     PetscCall(DMGetDS(dmc, &ds));
10010     PetscCall(PetscDSGetWeakForm(ds, &wf));
10011     PetscCall(PetscWeakFormClear(wf));
10012     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10013     PetscCall(DMCreateMatrix(dmc, mass));
10014     PetscCall(DMGetLocalVector(dmc, &u));
10015     PetscCall(DMPlexGetDepth(dmc, &depth));
10016     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10017     PetscCall(MatZeroEntries(*mass));
10018     key.label = NULL;
10019     key.value = 0;
10020     key.field = 0;
10021     key.part  = 0;
10022     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10023     PetscCall(ISDestroy(&cellIS));
10024     PetscCall(DMRestoreLocalVector(dmc, &u));
10025     PetscCall(DMDestroy(&dmc));
10026   } else {
10027     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10028     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10029     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10030     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10031 
10032     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10033     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10034     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10035     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10036 
10037     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10038     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10039     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10040     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10041   }
10042   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10043   PetscFunctionReturn(PETSC_SUCCESS);
10044 }
10045 
10046 /*@
10047   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10048 
10049   Input Parameter:
10050 . dm - The `DMPLEX` object
10051 
10052   Output Parameter:
10053 . regular - The flag
10054 
10055   Level: intermediate
10056 
10057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10058 @*/
10059 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10060 {
10061   PetscFunctionBegin;
10062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10063   PetscAssertPointer(regular, 2);
10064   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10065   PetscFunctionReturn(PETSC_SUCCESS);
10066 }
10067 
10068 /*@
10069   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10070 
10071   Input Parameters:
10072 + dm      - The `DMPLEX` object
10073 - regular - The flag
10074 
10075   Level: intermediate
10076 
10077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10078 @*/
10079 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10080 {
10081   PetscFunctionBegin;
10082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10083   ((DM_Plex *)dm->data)->regularRefinement = regular;
10084   PetscFunctionReturn(PETSC_SUCCESS);
10085 }
10086 
10087 /*@
10088   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10089   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10090 
10091   Not Collective
10092 
10093   Input Parameter:
10094 . dm - The `DMPLEX` object
10095 
10096   Output Parameters:
10097 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10098 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10099 
10100   Level: intermediate
10101 
10102 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10103 @*/
10104 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10105 {
10106   DM_Plex *plex = (DM_Plex *)dm->data;
10107 
10108   PetscFunctionBegin;
10109   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10110   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10111   if (anchorSection) *anchorSection = plex->anchorSection;
10112   if (anchorIS) *anchorIS = plex->anchorIS;
10113   PetscFunctionReturn(PETSC_SUCCESS);
10114 }
10115 
10116 /*@
10117   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10118 
10119   Collective
10120 
10121   Input Parameters:
10122 + dm            - The `DMPLEX` object
10123 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10124                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10125 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10126 
10127   Level: intermediate
10128 
10129   Notes:
10130   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10131   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10132   combination of other points' degrees of freedom.
10133 
10134   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10135   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10136 
10137   The reference counts of `anchorSection` and `anchorIS` are incremented.
10138 
10139 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10140 @*/
10141 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10142 {
10143   DM_Plex    *plex = (DM_Plex *)dm->data;
10144   PetscMPIInt result;
10145 
10146   PetscFunctionBegin;
10147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10148   if (anchorSection) {
10149     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10150     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10151     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10152   }
10153   if (anchorIS) {
10154     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10155     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10156     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10157   }
10158 
10159   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10160   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10161   plex->anchorSection = anchorSection;
10162 
10163   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10164   PetscCall(ISDestroy(&plex->anchorIS));
10165   plex->anchorIS = anchorIS;
10166 
10167   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10168     PetscInt        size, a, pStart, pEnd;
10169     const PetscInt *anchors;
10170 
10171     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10172     PetscCall(ISGetLocalSize(anchorIS, &size));
10173     PetscCall(ISGetIndices(anchorIS, &anchors));
10174     for (a = 0; a < size; a++) {
10175       PetscInt p;
10176 
10177       p = anchors[a];
10178       if (p >= pStart && p < pEnd) {
10179         PetscInt dof;
10180 
10181         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10182         if (dof) {
10183           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10184           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10185         }
10186       }
10187     }
10188     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10189   }
10190   /* reset the generic constraints */
10191   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10192   PetscFunctionReturn(PETSC_SUCCESS);
10193 }
10194 
10195 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10196 {
10197   PetscSection anchorSection;
10198   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10199 
10200   PetscFunctionBegin;
10201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10202   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10203   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10204   PetscCall(PetscSectionGetNumFields(section, &numFields));
10205   if (numFields) {
10206     PetscInt f;
10207     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10208 
10209     for (f = 0; f < numFields; f++) {
10210       PetscInt numComp;
10211 
10212       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10213       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10214     }
10215   }
10216   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10217   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10218   pStart = PetscMax(pStart, sStart);
10219   pEnd   = PetscMin(pEnd, sEnd);
10220   pEnd   = PetscMax(pStart, pEnd);
10221   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10222   for (p = pStart; p < pEnd; p++) {
10223     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10224     if (dof) {
10225       PetscCall(PetscSectionGetDof(section, p, &dof));
10226       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10227       for (f = 0; f < numFields; f++) {
10228         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10229         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10230       }
10231     }
10232   }
10233   PetscCall(PetscSectionSetUp(*cSec));
10234   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10235   PetscFunctionReturn(PETSC_SUCCESS);
10236 }
10237 
10238 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10239 {
10240   PetscSection    aSec;
10241   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10242   const PetscInt *anchors;
10243   PetscInt        numFields, f;
10244   IS              aIS;
10245   MatType         mtype;
10246   PetscBool       iscuda, iskokkos;
10247 
10248   PetscFunctionBegin;
10249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10250   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10251   PetscCall(PetscSectionGetStorageSize(section, &n));
10252   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10253   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10254   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10255   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10256   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10257   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10258   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10259   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10260   else mtype = MATSEQAIJ;
10261   PetscCall(MatSetType(*cMat, mtype));
10262   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10263   PetscCall(ISGetIndices(aIS, &anchors));
10264   /* cSec will be a subset of aSec and section */
10265   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10266   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10267   PetscCall(PetscMalloc1(m + 1, &i));
10268   i[0] = 0;
10269   PetscCall(PetscSectionGetNumFields(section, &numFields));
10270   for (p = pStart; p < pEnd; p++) {
10271     PetscInt rDof, rOff, r;
10272 
10273     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10274     if (!rDof) continue;
10275     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10276     if (numFields) {
10277       for (f = 0; f < numFields; f++) {
10278         annz = 0;
10279         for (r = 0; r < rDof; r++) {
10280           a = anchors[rOff + r];
10281           if (a < sStart || a >= sEnd) continue;
10282           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10283           annz += aDof;
10284         }
10285         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10286         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10287         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10288       }
10289     } else {
10290       annz = 0;
10291       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10292       for (q = 0; q < dof; q++) {
10293         a = anchors[rOff + q];
10294         if (a < sStart || a >= sEnd) continue;
10295         PetscCall(PetscSectionGetDof(section, a, &aDof));
10296         annz += aDof;
10297       }
10298       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10299       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10300       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10301     }
10302   }
10303   nnz = i[m];
10304   PetscCall(PetscMalloc1(nnz, &j));
10305   offset = 0;
10306   for (p = pStart; p < pEnd; p++) {
10307     if (numFields) {
10308       for (f = 0; f < numFields; f++) {
10309         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10310         for (q = 0; q < dof; q++) {
10311           PetscInt rDof, rOff, r;
10312           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10313           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10314           for (r = 0; r < rDof; r++) {
10315             PetscInt s;
10316 
10317             a = anchors[rOff + r];
10318             if (a < sStart || a >= sEnd) continue;
10319             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10320             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10321             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10322           }
10323         }
10324       }
10325     } else {
10326       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10327       for (q = 0; q < dof; q++) {
10328         PetscInt rDof, rOff, r;
10329         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10330         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10331         for (r = 0; r < rDof; r++) {
10332           PetscInt s;
10333 
10334           a = anchors[rOff + r];
10335           if (a < sStart || a >= sEnd) continue;
10336           PetscCall(PetscSectionGetDof(section, a, &aDof));
10337           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10338           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10339         }
10340       }
10341     }
10342   }
10343   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10344   PetscCall(PetscFree(i));
10345   PetscCall(PetscFree(j));
10346   PetscCall(ISRestoreIndices(aIS, &anchors));
10347   PetscFunctionReturn(PETSC_SUCCESS);
10348 }
10349 
10350 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10351 {
10352   DM_Plex     *plex = (DM_Plex *)dm->data;
10353   PetscSection anchorSection, section, cSec;
10354   Mat          cMat;
10355 
10356   PetscFunctionBegin;
10357   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10358   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10359   if (anchorSection) {
10360     PetscInt Nf;
10361 
10362     PetscCall(DMGetLocalSection(dm, &section));
10363     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10364     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10365     PetscCall(DMGetNumFields(dm, &Nf));
10366     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10367     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10368     PetscCall(PetscSectionDestroy(&cSec));
10369     PetscCall(MatDestroy(&cMat));
10370   }
10371   PetscFunctionReturn(PETSC_SUCCESS);
10372 }
10373 
10374 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10375 {
10376   IS           subis;
10377   PetscSection section, subsection;
10378 
10379   PetscFunctionBegin;
10380   PetscCall(DMGetLocalSection(dm, &section));
10381   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10382   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10383   /* Create subdomain */
10384   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10385   /* Create submodel */
10386   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10387   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10388   PetscCall(DMSetLocalSection(*subdm, subsection));
10389   PetscCall(PetscSectionDestroy(&subsection));
10390   PetscCall(DMCopyDisc(dm, *subdm));
10391   /* Create map from submodel to global model */
10392   if (is) {
10393     PetscSection    sectionGlobal, subsectionGlobal;
10394     IS              spIS;
10395     const PetscInt *spmap;
10396     PetscInt       *subIndices;
10397     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10398     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10399 
10400     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10401     PetscCall(ISGetIndices(spIS, &spmap));
10402     PetscCall(PetscSectionGetNumFields(section, &Nf));
10403     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10404     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10405     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10406     for (p = pStart; p < pEnd; ++p) {
10407       PetscInt gdof, pSubSize = 0;
10408 
10409       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10410       if (gdof > 0) {
10411         for (f = 0; f < Nf; ++f) {
10412           PetscInt fdof, fcdof;
10413 
10414           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10415           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10416           pSubSize += fdof - fcdof;
10417         }
10418         subSize += pSubSize;
10419         if (pSubSize) {
10420           if (bs < 0) {
10421             bs = pSubSize;
10422           } else if (bs != pSubSize) {
10423             /* Layout does not admit a pointwise block size */
10424             bs = 1;
10425           }
10426         }
10427       }
10428     }
10429     /* Must have same blocksize on all procs (some might have no points) */
10430     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10431     bsLocal[1] = bs;
10432     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10433     if (bsMinMax[0] != bsMinMax[1]) {
10434       bs = 1;
10435     } else {
10436       bs = bsMinMax[0];
10437     }
10438     PetscCall(PetscMalloc1(subSize, &subIndices));
10439     for (p = pStart; p < pEnd; ++p) {
10440       PetscInt gdof, goff;
10441 
10442       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10443       if (gdof > 0) {
10444         const PetscInt point = spmap[p];
10445 
10446         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10447         for (f = 0; f < Nf; ++f) {
10448           PetscInt fdof, fcdof, fc, f2, poff = 0;
10449 
10450           /* Can get rid of this loop by storing field information in the global section */
10451           for (f2 = 0; f2 < f; ++f2) {
10452             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10453             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10454             poff += fdof - fcdof;
10455           }
10456           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10457           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10458           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10459         }
10460       }
10461     }
10462     PetscCall(ISRestoreIndices(spIS, &spmap));
10463     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10464     if (bs > 1) {
10465       /* We need to check that the block size does not come from non-contiguous fields */
10466       PetscInt i, j, set = 1;
10467       for (i = 0; i < subSize; i += bs) {
10468         for (j = 0; j < bs; ++j) {
10469           if (subIndices[i + j] != subIndices[i] + j) {
10470             set = 0;
10471             break;
10472           }
10473         }
10474       }
10475       if (set) PetscCall(ISSetBlockSize(*is, bs));
10476     }
10477     /* Attach nullspace */
10478     for (f = 0; f < Nf; ++f) {
10479       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10480       if ((*subdm)->nullspaceConstructors[f]) break;
10481     }
10482     if (f < Nf) {
10483       MatNullSpace nullSpace;
10484       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10485 
10486       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10487       PetscCall(MatNullSpaceDestroy(&nullSpace));
10488     }
10489   }
10490   PetscFunctionReturn(PETSC_SUCCESS);
10491 }
10492 
10493 /*@
10494   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10495 
10496   Input Parameters:
10497 + dm    - The `DM`
10498 - dummy - unused argument
10499 
10500   Options Database Key:
10501 . -dm_plex_monitor_throughput - Activate the monitor
10502 
10503   Level: developer
10504 
10505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10506 @*/
10507 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10508 {
10509   PetscLogHandler default_handler;
10510 
10511   PetscFunctionBegin;
10512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10513   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10514   if (default_handler) {
10515     PetscLogEvent      event;
10516     PetscEventPerfInfo eventInfo;
10517     PetscReal          cellRate, flopRate;
10518     PetscInt           cStart, cEnd, Nf, N;
10519     const char        *name;
10520 
10521     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10522     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10523     PetscCall(DMGetNumFields(dm, &Nf));
10524     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10525     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10526     N        = (cEnd - cStart) * Nf * eventInfo.count;
10527     flopRate = eventInfo.flops / eventInfo.time;
10528     cellRate = N / eventInfo.time;
10529     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)));
10530   } else {
10531     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.");
10532   }
10533   PetscFunctionReturn(PETSC_SUCCESS);
10534 }
10535