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