xref: /petsc/src/dm/impls/plex/plex.c (revision 85f25e71687eef93ec659366612fd9d9ee335aac)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
89 
90   PetscFunctionBegin;
91   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
93   PetscCall(ISGetLocalSize(valueIS, &Nct));
94   PetscCall(ISGetIndices(valueIS, &ctypes));
95   if (!Nct) cS = cE = 0;
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS = PetscMin(cS, ctS);
112     cE = PetscMax(cE, ctE);
113   }
114   PetscCall(ISDestroy(&valueIS));
115   // Reset label for fast lookup
116   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
117   if (cStart) *cStart = cS;
118   if (cEnd) *cEnd = cE;
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
123 {
124   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
125   PetscInt                *sStart, *sEnd;
126   PetscViewerVTKFieldType *ft;
127   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
128   DMLabel                  depthLabel, ctLabel;
129 
130   PetscFunctionBegin;
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161   }
162 
163   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
164   *types = 0;
165 
166   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
167     if (globalvcdof[c]) ++(*types);
168   }
169 
170   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
171   t = 0;
172   if (globalvcdof[DM_NUM_POLYTOPES]) {
173     sStart[t] = vStart;
174     sEnd[t]   = vEnd;
175     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
176     ++t;
177   }
178 
179   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
180     if (globalvcdof[c]) {
181       const DMPolytopeType ict = (DMPolytopeType)c;
182 
183       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
184       sStart[t] = cStart;
185       sEnd[t]   = cEnd;
186       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
187       ++t;
188     }
189   }
190 
191   if (!*types) {
192     if (field >= 0) {
193       const char *fieldname;
194 
195       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
196       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
197     } else {
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
199     }
200   }
201 
202   *ssStart = sStart;
203   *ssEnd   = sEnd;
204   *sft     = ft;
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
209 {
210   PetscFunctionBegin;
211   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
216 {
217   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
218   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
219 
220   PetscFunctionBegin;
221   *ft = PETSC_VTK_INVALID;
222   PetscCall(DMGetCoordinateDim(dm, &cdim));
223   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
224   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
225   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
226   if (field >= 0) {
227     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
228     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
229   } else {
230     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
231     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
232   }
233   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
234   if (globalvcdof[0]) {
235     *sStart = vStart;
236     *sEnd   = vEnd;
237     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
238     else *ft = PETSC_VTK_POINT_FIELD;
239   } else if (globalvcdof[1]) {
240     *sStart = cStart;
241     *sEnd   = cEnd;
242     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
243     else *ft = PETSC_VTK_CELL_FIELD;
244   } else {
245     if (field >= 0) {
246       const char *fieldname;
247 
248       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
249       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
250     } else {
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
252     }
253   }
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 /*@
258   DMPlexVecView1D - Plot many 1D solutions on the same line graph
259 
260   Collective
261 
262   Input Parameters:
263 + dm     - The `DMPLEX` object
264 . n      - The number of vectors
265 . u      - The array of local vectors
266 - viewer - The `PetscViewer`
267 
268   Level: advanced
269 
270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
271 @*/
272 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
273 {
274   PetscDS            ds;
275   PetscDraw          draw = NULL;
276   PetscDrawLG        lg;
277   Vec                coordinates;
278   const PetscScalar *coords, **sol;
279   PetscReal         *vals;
280   PetscInt          *Nc;
281   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
282   char             **names;
283 
284   PetscFunctionBegin;
285   PetscCall(DMGetDS(dm, &ds));
286   PetscCall(PetscDSGetNumFields(ds, &Nf));
287   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
288   PetscCall(PetscDSGetComponents(ds, &Nc));
289 
290   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
291   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
292   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
293 
294   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
295   for (i = 0, l = 0; i < n; ++i) {
296     const char *vname;
297 
298     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
299     for (f = 0; f < Nf; ++f) {
300       PetscObject disc;
301       const char *fname;
302       char        tmpname[PETSC_MAX_PATH_LEN];
303 
304       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
305       /* TODO Create names for components */
306       for (c = 0; c < Nc[f]; ++c, ++l) {
307         PetscCall(PetscObjectGetName(disc, &fname));
308         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
311         PetscCall(PetscStrallocpy(tmpname, &names[l]));
312       }
313     }
314   }
315   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
316   /* Just add P_1 support for now */
317   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
318   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
319   PetscCall(VecGetArrayRead(coordinates, &coords));
320   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
321   for (v = vStart; v < vEnd; ++v) {
322     PetscScalar *x, *svals;
323 
324     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
325     for (i = 0; i < n; ++i) {
326       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
327       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
328     }
329     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
330   }
331   PetscCall(VecRestoreArrayRead(coordinates, &coords));
332   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
333   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
334   PetscCall(PetscFree3(sol, names, vals));
335 
336   PetscCall(PetscDrawLGDraw(lg));
337   PetscCall(PetscDrawLGDestroy(&lg));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
342 {
343   DM dm;
344 
345   PetscFunctionBegin;
346   PetscCall(VecGetDM(u, &dm));
347   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
352 {
353   DM                 dm;
354   PetscSection       s;
355   PetscDraw          draw, popup;
356   DM                 cdm;
357   PetscSection       coordSection;
358   Vec                coordinates;
359   const PetscScalar *array;
360   PetscReal          lbound[3], ubound[3];
361   PetscReal          vbound[2], time;
362   PetscBool          flg;
363   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
364   const char        *name;
365   char               title[PETSC_MAX_PATH_LEN];
366 
367   PetscFunctionBegin;
368   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
369   PetscCall(VecGetDM(v, &dm));
370   PetscCall(DMGetCoordinateDim(dm, &dim));
371   PetscCall(DMGetLocalSection(dm, &s));
372   PetscCall(PetscSectionGetNumFields(s, &Nf));
373   PetscCall(DMGetCoarsenLevel(dm, &level));
374   PetscCall(DMGetCoordinateDM(dm, &cdm));
375   PetscCall(DMGetLocalSection(cdm, &coordSection));
376   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
377   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
378   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
379 
380   PetscCall(PetscObjectGetName((PetscObject)v, &name));
381   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
382 
383   PetscCall(VecGetLocalSize(coordinates, &N));
384   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
385   PetscCall(PetscDrawClear(draw));
386 
387   /* Could implement something like DMDASelectFields() */
388   for (f = 0; f < Nf; ++f) {
389     DM          fdm = dm;
390     Vec         fv  = v;
391     IS          fis;
392     char        prefix[PETSC_MAX_PATH_LEN];
393     const char *fname;
394 
395     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
396     PetscCall(PetscSectionGetFieldName(s, f, &fname));
397 
398     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
399     else prefix[0] = '\0';
400     if (Nf > 1) {
401       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
402       PetscCall(VecGetSubVector(v, fis, &fv));
403       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
404       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
405     }
406     for (comp = 0; comp < Nc; ++comp, ++w) {
407       PetscInt nmax = 2;
408 
409       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
410       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
411       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
412       PetscCall(PetscDrawSetTitle(draw, title));
413 
414       /* TODO Get max and min only for this component */
415       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
416       if (!flg) {
417         PetscCall(VecMin(fv, NULL, &vbound[0]));
418         PetscCall(VecMax(fv, NULL, &vbound[1]));
419         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
420       }
421 
422       PetscCall(PetscDrawGetPopup(draw, &popup));
423       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
424       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
425       PetscCall(VecGetArrayRead(fv, &array));
426       for (c = cStart; c < cEnd; ++c) {
427         PetscScalar       *coords = NULL, *a = NULL;
428         const PetscScalar *coords_arr;
429         PetscBool          isDG;
430         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
431 
432         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
433         if (a) {
434           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
435           color[1] = color[2] = color[3] = color[0];
436         } else {
437           PetscScalar *vals = NULL;
438           PetscInt     numVals, va;
439 
440           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
441           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
442           switch (numVals / Nc) {
443           case 3: /* P1 Triangle */
444           case 4: /* P1 Quadrangle */
445             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
446             break;
447           case 6: /* P2 Triangle */
448           case 8: /* P2 Quadrangle */
449             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
450             break;
451           default:
452             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
453           }
454           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
455         }
456         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
457         switch (numCoords) {
458         case 6:
459         case 12: /* Localized triangle */
460           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
461           break;
462         case 8:
463         case 16: /* Localized quadrilateral */
464           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
465           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
466           break;
467         default:
468           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
469         }
470         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
471       }
472       PetscCall(VecRestoreArrayRead(fv, &array));
473       PetscCall(PetscDrawFlush(draw));
474       PetscCall(PetscDrawPause(draw));
475       PetscCall(PetscDrawSave(draw));
476     }
477     if (Nf > 1) {
478       PetscCall(VecRestoreSubVector(v, fis, &fv));
479       PetscCall(ISDestroy(&fis));
480       PetscCall(DMDestroy(&fdm));
481     }
482   }
483   PetscFunctionReturn(PETSC_SUCCESS);
484 }
485 
486 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
487 {
488   DM        dm;
489   PetscDraw draw;
490   PetscInt  dim;
491   PetscBool isnull;
492 
493   PetscFunctionBegin;
494   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
495   PetscCall(PetscDrawIsNull(draw, &isnull));
496   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
497 
498   PetscCall(VecGetDM(v, &dm));
499   PetscCall(DMGetCoordinateDim(dm, &dim));
500   switch (dim) {
501   case 1:
502     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
503     break;
504   case 2:
505     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
506     break;
507   default:
508     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
509   }
510   PetscFunctionReturn(PETSC_SUCCESS);
511 }
512 
513 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
514 {
515   DM                      dm;
516   Vec                     locv;
517   const char             *name;
518   PetscSection            section;
519   PetscInt                pStart, pEnd;
520   PetscInt                numFields;
521   PetscViewerVTKFieldType ft;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
526   PetscCall(PetscObjectGetName((PetscObject)v, &name));
527   PetscCall(PetscObjectSetName((PetscObject)locv, name));
528   PetscCall(VecCopy(v, locv));
529   PetscCall(DMGetLocalSection(dm, &section));
530   PetscCall(PetscSectionGetNumFields(section, &numFields));
531   if (!numFields) {
532     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
533     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
534   } else {
535     PetscInt f;
536 
537     for (f = 0; f < numFields; f++) {
538       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
539       if (ft == PETSC_VTK_INVALID) continue;
540       PetscCall(PetscObjectReference((PetscObject)locv));
541       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
542     }
543     PetscCall(VecDestroy(&locv));
544   }
545   PetscFunctionReturn(PETSC_SUCCESS);
546 }
547 
548 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
549 {
550   DM        dm;
551   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(v, &dm));
555   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
561   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
562     PetscInt    i, numFields;
563     PetscObject fe;
564     PetscBool   fem  = PETSC_FALSE;
565     Vec         locv = v;
566     const char *name;
567     PetscInt    step;
568     PetscReal   time;
569 
570     PetscCall(DMGetNumFields(dm, &numFields));
571     for (i = 0; i < numFields; i++) {
572       PetscCall(DMGetField(dm, i, NULL, &fe));
573       if (fe->classid == PETSCFE_CLASSID) {
574         fem = PETSC_TRUE;
575         break;
576       }
577     }
578     if (fem) {
579       PetscObject isZero;
580 
581       PetscCall(DMGetLocalVector(dm, &locv));
582       PetscCall(PetscObjectGetName((PetscObject)v, &name));
583       PetscCall(PetscObjectSetName((PetscObject)locv, name));
584       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
585       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
586       PetscCall(VecCopy(v, locv));
587       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
588       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
589     }
590     if (isvtk) {
591       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
592     } else if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
595 #else
596       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598     } else if (isdraw) {
599       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
600     } else if (isglvis) {
601       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
602       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
603       PetscCall(VecView_GLVis(locv, viewer));
604     } else if (iscgns) {
605 #if defined(PETSC_HAVE_CGNS)
606       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
607 #else
608       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
609 #endif
610     }
611     if (fem) {
612       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
613       PetscCall(DMRestoreLocalVector(dm, &locv));
614     }
615   } else {
616     PetscBool isseq;
617 
618     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
619     if (isseq) PetscCall(VecView_Seq(v, viewer));
620     else PetscCall(VecView_MPI(v, viewer));
621   }
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
626 {
627   DM        dm;
628   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (isvtk || isdraw || isglvis || iscgns) {
640     Vec         locv;
641     PetscObject isZero;
642     const char *name;
643 
644     PetscCall(DMGetLocalVector(dm, &locv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)locv, name));
647     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
648     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
649     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
650     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
651     PetscCall(VecView_Plex_Local(locv, viewer));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
653     PetscCall(DMRestoreLocalVector(dm, &locv));
654   } else if (ishdf5) {
655 #if defined(PETSC_HAVE_HDF5)
656     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
657 #else
658     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
659 #endif
660   } else if (isexodusii) {
661 #if defined(PETSC_HAVE_EXODUSII)
662     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
665 #endif
666   } else {
667     PetscBool isseq;
668 
669     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
670     if (isseq) PetscCall(VecView_Seq(v, viewer));
671     else PetscCall(VecView_MPI(v, viewer));
672   }
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   MPI_Comm          comm;
680   PetscViewerFormat format;
681   Vec               v;
682   PetscBool         isvtk, ishdf5;
683 
684   PetscFunctionBegin;
685   PetscCall(VecGetDM(originalv, &dm));
686   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
687   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
691   if (format == PETSC_VIEWER_NATIVE) {
692     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
693     /* this need a better fix */
694     if (dm->useNatural) {
695       if (dm->sfNatural) {
696         const char *vecname;
697         PetscInt    n, nroots;
698 
699         PetscCall(VecGetLocalSize(originalv, &n));
700         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
701         if (n == nroots) {
702           PetscCall(DMPlexCreateNaturalVector(dm, &v));
703           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
704           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
705           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
706           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
707         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
708       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
709     } else v = originalv;
710   } else v = originalv;
711 
712   if (ishdf5) {
713 #if defined(PETSC_HAVE_HDF5)
714     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
715 #else
716     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
717 #endif
718   } else if (isvtk) {
719     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
720   } else {
721     PetscBool isseq;
722 
723     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
724     if (isseq) PetscCall(VecView_Seq(v, viewer));
725     else PetscCall(VecView_MPI(v, viewer));
726   }
727   if (v != originalv) PetscCall(VecDestroy(&v));
728   PetscFunctionReturn(PETSC_SUCCESS);
729 }
730 
731 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
732 {
733   DM        dm;
734   PetscBool ishdf5;
735 
736   PetscFunctionBegin;
737   PetscCall(VecGetDM(v, &dm));
738   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
739   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
740   if (ishdf5) {
741     DM          dmBC;
742     Vec         gv;
743     const char *name;
744 
745     PetscCall(DMGetOutputDM(dm, &dmBC));
746     PetscCall(DMGetGlobalVector(dmBC, &gv));
747     PetscCall(PetscObjectGetName((PetscObject)v, &name));
748     PetscCall(PetscObjectSetName((PetscObject)gv, name));
749     PetscCall(VecLoad_Default(gv, viewer));
750     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
753   } else PetscCall(VecLoad_Default(v, viewer));
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
758 {
759   DM        dm;
760   PetscBool ishdf5, isexodusii;
761 
762   PetscFunctionBegin;
763   PetscCall(VecGetDM(v, &dm));
764   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
767   if (ishdf5) {
768 #if defined(PETSC_HAVE_HDF5)
769     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
770 #else
771     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
772 #endif
773   } else if (isexodusii) {
774 #if defined(PETSC_HAVE_EXODUSII)
775     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
776 #else
777     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
778 #endif
779   } else PetscCall(VecLoad_Default(v, viewer));
780   PetscFunctionReturn(PETSC_SUCCESS);
781 }
782 
783 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
784 {
785   DM                dm;
786   PetscViewerFormat format;
787   PetscBool         ishdf5;
788 
789   PetscFunctionBegin;
790   PetscCall(VecGetDM(originalv, &dm));
791   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
792   PetscCall(PetscViewerGetFormat(viewer, &format));
793   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
794   if (format == PETSC_VIEWER_NATIVE) {
795     if (dm->useNatural) {
796       if (dm->sfNatural) {
797         if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799           Vec         v;
800           const char *vecname;
801 
802           PetscCall(DMPlexCreateNaturalVector(dm, &v));
803           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
804           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
805           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
806           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
807           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
808           PetscCall(VecDestroy(&v));
809 #else
810           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
811 #endif
812         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
813       }
814     } else PetscCall(VecLoad_Default(originalv, viewer));
815   }
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
820 {
821   PetscSection       coordSection;
822   Vec                coordinates;
823   DMLabel            depthLabel, celltypeLabel;
824   const char        *name[4];
825   const PetscScalar *a;
826   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
827 
828   PetscFunctionBegin;
829   PetscCall(DMGetDimension(dm, &dim));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCoordinateSection(dm, &coordSection));
832   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
833   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
834   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
835   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
836   PetscCall(VecGetArrayRead(coordinates, &a));
837   name[0]       = "vertex";
838   name[1]       = "edge";
839   name[dim - 1] = "face";
840   name[dim]     = "cell";
841   for (c = cStart; c < cEnd; ++c) {
842     PetscInt *closure = NULL;
843     PetscInt  closureSize, cl, ct;
844 
845     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
847     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
848     PetscCall(PetscViewerASCIIPushTab(viewer));
849     for (cl = 0; cl < closureSize * 2; cl += 2) {
850       PetscInt point = closure[cl], depth, dof, off, d, p;
851 
852       if ((point < pStart) || (point >= pEnd)) continue;
853       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
854       if (!dof) continue;
855       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
856       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
858       for (p = 0; p < dof / dim; ++p) {
859         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
860         for (d = 0; d < dim; ++d) {
861           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
862           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
863         }
864         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
865       }
866       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
867     }
868     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
869     PetscCall(PetscViewerASCIIPopTab(viewer));
870   }
871   PetscCall(VecRestoreArrayRead(coordinates, &a));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 typedef enum {
876   CS_CARTESIAN,
877   CS_POLAR,
878   CS_CYLINDRICAL,
879   CS_SPHERICAL
880 } CoordSystem;
881 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
882 
883 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
884 {
885   PetscInt i;
886 
887   PetscFunctionBegin;
888   if (dim > 3) {
889     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
890   } else {
891     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
892 
893     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
894     switch (cs) {
895     case CS_CARTESIAN:
896       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
897       break;
898     case CS_POLAR:
899       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
900       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
901       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
902       break;
903     case CS_CYLINDRICAL:
904       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
905       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
906       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
907       trcoords[2] = coords[2];
908       break;
909     case CS_SPHERICAL:
910       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
911       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
912       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
913       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
914       break;
915     }
916     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
917   }
918   PetscFunctionReturn(PETSC_SUCCESS);
919 }
920 
921 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
922 {
923   DM_Plex          *mesh = (DM_Plex *)dm->data;
924   DM                cdm, cdmCell;
925   PetscSection      coordSection, coordSectionCell;
926   Vec               coordinates, coordinatesCell;
927   PetscViewerFormat format;
928 
929   PetscFunctionBegin;
930   PetscCall(PetscViewerGetFormat(viewer, &format));
931   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
932     const char *name;
933     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
934     PetscInt    pStart, pEnd, p, numLabels, l;
935     PetscMPIInt rank, size;
936 
937     PetscCall(DMGetCoordinateDM(dm, &cdm));
938     PetscCall(DMGetCoordinateSection(dm, &coordSection));
939     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
940     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
941     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
942     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
943     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
944     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
945     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
946     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
947     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
948     PetscCall(DMGetDimension(dm, &dim));
949     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
950     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
951     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
952     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
953     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
954     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
955     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
956     for (p = pStart; p < pEnd; ++p) {
957       PetscInt dof, off, s;
958 
959       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
960       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
961       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
962     }
963     PetscCall(PetscViewerFlush(viewer));
964     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
965     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
966     for (p = pStart; p < pEnd; ++p) {
967       PetscInt dof, off, c;
968 
969       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
970       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
971       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
972     }
973     PetscCall(PetscViewerFlush(viewer));
974     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
975     if (coordSection && coordinates) {
976       CoordSystem        cs = CS_CARTESIAN;
977       const PetscScalar *array, *arrayCell = NULL;
978       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
979       PetscMPIInt        rank;
980       const char        *name;
981 
982       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
983       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
984       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
985       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
986       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
987       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
988       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
989       pStart = PetscMin(pvStart, pcStart);
990       pEnd   = PetscMax(pvEnd, pcEnd);
991       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
994       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
995 
996       PetscCall(VecGetArrayRead(coordinates, &array));
997       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
998       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
999       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1000       for (p = pStart; p < pEnd; ++p) {
1001         PetscInt dof, off;
1002 
1003         if (p >= pvStart && p < pvEnd) {
1004           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1005           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1006           if (dof) {
1007             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1008             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1010           }
1011         }
1012         if (cdmCell && p >= pcStart && p < pcEnd) {
1013           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1014           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1015           if (dof) {
1016             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1017             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1019           }
1020         }
1021       }
1022       PetscCall(PetscViewerFlush(viewer));
1023       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1024       PetscCall(VecRestoreArrayRead(coordinates, &array));
1025       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1026     }
1027     PetscCall(DMGetNumLabels(dm, &numLabels));
1028     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1029     for (l = 0; l < numLabels; ++l) {
1030       DMLabel     label;
1031       PetscBool   isdepth;
1032       const char *name;
1033 
1034       PetscCall(DMGetLabelName(dm, l, &name));
1035       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1036       if (isdepth) continue;
1037       PetscCall(DMGetLabel(dm, name, &label));
1038       PetscCall(DMLabelView(label, viewer));
1039     }
1040     if (size > 1) {
1041       PetscSF sf;
1042 
1043       PetscCall(DMGetPointSF(dm, &sf));
1044       PetscCall(PetscSFView(sf, viewer));
1045     }
1046     if (mesh->periodic.face_sfs)
1047       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     if (lflg) {
1115       DMLabel lbl;
1116 
1117       PetscCall(DMGetLabel(dm, lname, &lbl));
1118       if (lbl) {
1119         PetscInt val, defval;
1120 
1121         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1122         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1123         for (c = pStart; c < pEnd; c++) {
1124           PetscInt *closure = NULL;
1125           PetscInt  closureSize;
1126 
1127           PetscCall(DMLabelGetValue(lbl, c, &val));
1128           if (val == defval) continue;
1129 
1130           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1132           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1133         }
1134       }
1135     }
1136 
1137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1138     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1139     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1140     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1141 \\documentclass[tikz]{standalone}\n\n\
1142 \\usepackage{pgflibraryshapes}\n\
1143 \\usetikzlibrary{backgrounds}\n\
1144 \\usetikzlibrary{arrows}\n\
1145 \\begin{document}\n"));
1146     if (size > 1) {
1147       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1148       for (p = 0; p < size; ++p) {
1149         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1150         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1151       }
1152       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1153     }
1154     if (drawHasse) {
1155       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1156 
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1169     }
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1171 
1172     /* Plot vertices */
1173     PetscCall(VecGetArray(coordinates, &coords));
1174     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1175     for (v = vStart; v < vEnd; ++v) {
1176       PetscInt  off, dof, d;
1177       PetscBool isLabeled = PETSC_FALSE;
1178 
1179       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1180       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1181       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1182       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1183       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1184       for (d = 0; d < dof; ++d) {
1185         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1186         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1187       }
1188       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1189       if (dim == 3) {
1190         PetscReal tmp = tcoords[1];
1191         tcoords[1]    = tcoords[2];
1192         tcoords[2]    = -tmp;
1193       }
1194       for (d = 0; d < dof; ++d) {
1195         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1196         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1197       }
1198       if (drawHasse) color = colors[0 % numColors];
1199       else color = colors[rank % numColors];
1200       for (l = 0; l < numLabels; ++l) {
1201         PetscInt val;
1202         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1203         if (val >= 0) {
1204           color     = lcolors[l % numLColors];
1205           isLabeled = PETSC_TRUE;
1206           break;
1207         }
1208       }
1209       if (drawNumbers[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1211       } else if (drawColors[0]) {
1212         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1213       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1214     }
1215     PetscCall(VecRestoreArray(coordinates, &coords));
1216     PetscCall(PetscViewerFlush(viewer));
1217     /* Plot edges */
1218     if (plotEdges) {
1219       PetscCall(VecGetArray(coordinates, &coords));
1220       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1221       for (e = eStart; e < eEnd; ++e) {
1222         const PetscInt *cone;
1223         PetscInt        coneSize, offA, offB, dof, d;
1224 
1225         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1226         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1227         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1228         PetscCall(DMPlexGetCone(dm, e, &cone));
1229         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1231         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1232         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1233         for (d = 0; d < dof; ++d) {
1234           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1235           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1236         }
1237         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1238         if (dim == 3) {
1239           PetscReal tmp = tcoords[1];
1240           tcoords[1]    = tcoords[2];
1241           tcoords[2]    = -tmp;
1242         }
1243         for (d = 0; d < dof; ++d) {
1244           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1245           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1246         }
1247         if (drawHasse) color = colors[1 % numColors];
1248         else color = colors[rank % numColors];
1249         for (l = 0; l < numLabels; ++l) {
1250           PetscInt val;
1251           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1252           if (val >= 0) {
1253             color = lcolors[l % numLColors];
1254             break;
1255           }
1256         }
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1258       }
1259       PetscCall(VecRestoreArray(coordinates, &coords));
1260       PetscCall(PetscViewerFlush(viewer));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1262     }
1263     /* Plot cells */
1264     if (dim == 3 || !drawNumbers[1]) {
1265       for (e = eStart; e < eEnd; ++e) {
1266         const PetscInt *cone;
1267 
1268         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1269         color = colors[rank % numColors];
1270         for (l = 0; l < numLabels; ++l) {
1271           PetscInt val;
1272           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1273           if (val >= 0) {
1274             color = lcolors[l % numLColors];
1275             break;
1276           }
1277         }
1278         PetscCall(DMPlexGetCone(dm, e, &cone));
1279         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1280       }
1281     } else {
1282       DMPolytopeType ct;
1283 
1284       /* Drawing a 2D polygon */
1285       for (c = cStart; c < cEnd; ++c) {
1286         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1287         PetscCall(DMPlexGetCellType(dm, c, &ct));
1288         if (DMPolytopeTypeIsHybrid(ct)) {
1289           const PetscInt *cone;
1290           PetscInt        coneSize, e;
1291 
1292           PetscCall(DMPlexGetCone(dm, c, &cone));
1293           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1294           for (e = 0; e < coneSize; ++e) {
1295             const PetscInt *econe;
1296 
1297             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1298             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1299           }
1300         } else {
1301           PetscInt *closure = NULL;
1302           PetscInt  closureSize, Nv = 0, v;
1303 
1304           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1305           for (p = 0; p < closureSize * 2; p += 2) {
1306             const PetscInt point = closure[p];
1307 
1308             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1309           }
1310           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1311           for (v = 0; v <= Nv; ++v) {
1312             const PetscInt vertex = closure[v % Nv];
1313 
1314             if (v > 0) {
1315               if (plotEdges) {
1316                 const PetscInt *edge;
1317                 PetscInt        endpoints[2], ne;
1318 
1319                 endpoints[0] = closure[v - 1];
1320                 endpoints[1] = vertex;
1321                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1322                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1323                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1324                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1325               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1326             }
1327             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1328           }
1329           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1330           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331         }
1332       }
1333     }
1334     for (c = cStart; c < cEnd; ++c) {
1335       double             ccoords[3] = {0.0, 0.0, 0.0};
1336       PetscBool          isLabeled  = PETSC_FALSE;
1337       PetscScalar       *cellCoords = NULL;
1338       const PetscScalar *array;
1339       PetscInt           numCoords, cdim, d;
1340       PetscBool          isDG;
1341 
1342       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1343       PetscCall(DMGetCoordinateDim(dm, &cdim));
1344       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1345       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1346       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1347       for (p = 0; p < numCoords / cdim; ++p) {
1348         for (d = 0; d < cdim; ++d) {
1349           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1350           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1351         }
1352         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1353         if (cdim == 3) {
1354           PetscReal tmp = tcoords[1];
1355           tcoords[1]    = tcoords[2];
1356           tcoords[2]    = -tmp;
1357         }
1358         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1359       }
1360       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1361       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1362       for (d = 0; d < cdim; ++d) {
1363         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1364         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1365       }
1366       if (drawHasse) color = colors[depth % numColors];
1367       else color = colors[rank % numColors];
1368       for (l = 0; l < numLabels; ++l) {
1369         PetscInt val;
1370         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1371         if (val >= 0) {
1372           color     = lcolors[l % numLColors];
1373           isLabeled = PETSC_TRUE;
1374           break;
1375         }
1376       }
1377       if (drawNumbers[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1379       } else if (drawColors[dim]) {
1380         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1381       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1382     }
1383     if (drawHasse) {
1384       color = colors[depth % numColors];
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1389       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1390 
1391       color = colors[1 % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       color = colors[0 % numColors];
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1403       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1404 
1405       for (p = pStart; p < pEnd; ++p) {
1406         const PetscInt *cone;
1407         PetscInt        coneSize, cp;
1408 
1409         PetscCall(DMPlexGetCone(dm, p, &cone));
1410         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1411         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1412       }
1413     }
1414     PetscCall(PetscViewerFlush(viewer));
1415     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1417     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1418     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1419     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1420     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1421     PetscCall(PetscFree3(names, colors, lcolors));
1422     PetscCall(PetscBTDestroy(&wp));
1423   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1424     Vec                    cown, acown;
1425     VecScatter             sct;
1426     ISLocalToGlobalMapping g2l;
1427     IS                     gid, acis;
1428     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1429     MPI_Group              ggroup, ngroup;
1430     PetscScalar           *array, nid;
1431     const PetscInt        *idxs;
1432     PetscInt              *idxs2, *start, *adjacency, *work;
1433     PetscInt64             lm[3], gm[3];
1434     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1435     PetscMPIInt            d1, d2, rank;
1436 
1437     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1438     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1439 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1440     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1441 #endif
1442     if (ncomm != MPI_COMM_NULL) {
1443       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1444       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1445       d1 = 0;
1446       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1447       nid = d2;
1448       PetscCallMPI(MPI_Group_free(&ggroup));
1449       PetscCallMPI(MPI_Group_free(&ngroup));
1450       PetscCallMPI(MPI_Comm_free(&ncomm));
1451     } else nid = 0.0;
1452 
1453     /* Get connectivity */
1454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1455     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1456 
1457     /* filter overlapped local cells */
1458     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1459     PetscCall(ISGetIndices(gid, &idxs));
1460     PetscCall(ISGetLocalSize(gid, &cum));
1461     PetscCall(PetscMalloc1(cum, &idxs2));
1462     for (c = cStart, cum = 0; c < cEnd; c++) {
1463       if (idxs[c - cStart] < 0) continue;
1464       idxs2[cum++] = idxs[c - cStart];
1465     }
1466     PetscCall(ISRestoreIndices(gid, &idxs));
1467     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1468     PetscCall(ISDestroy(&gid));
1469     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1470 
1471     /* support for node-aware cell locality */
1472     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1473     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1474     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1475     PetscCall(VecGetArray(cown, &array));
1476     for (c = 0; c < numVertices; c++) array[c] = nid;
1477     PetscCall(VecRestoreArray(cown, &array));
1478     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1479     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1481     PetscCall(ISDestroy(&acis));
1482     PetscCall(VecScatterDestroy(&sct));
1483     PetscCall(VecDestroy(&cown));
1484 
1485     /* compute edgeCut */
1486     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1487     PetscCall(PetscMalloc1(cum, &work));
1488     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1489     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1490     PetscCall(ISDestroy(&gid));
1491     PetscCall(VecGetArray(acown, &array));
1492     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1493       PetscInt totl;
1494 
1495       totl = start[c + 1] - start[c];
1496       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1497       for (i = 0; i < totl; i++) {
1498         if (work[i] < 0) {
1499           ect += 1;
1500           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1501         }
1502       }
1503     }
1504     PetscCall(PetscFree(work));
1505     PetscCall(VecRestoreArray(acown, &array));
1506     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1507     lm[1] = -numVertices;
1508     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1509     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1510     lm[0] = ect;                     /* edgeCut */
1511     lm[1] = ectn;                    /* node-aware edgeCut */
1512     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1513     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1514     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1515 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1517 #else
1518     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1519 #endif
1520     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1521     PetscCall(PetscFree(start));
1522     PetscCall(PetscFree(adjacency));
1523     PetscCall(VecDestroy(&acown));
1524   } else {
1525     const char    *name;
1526     PetscInt      *sizes, *hybsizes, *ghostsizes;
1527     PetscInt       locDepth, depth, cellHeight, dim, d;
1528     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1529     PetscInt       numLabels, l, maxSize = 17;
1530     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1531     MPI_Comm       comm;
1532     PetscMPIInt    size, rank;
1533 
1534     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1535     PetscCallMPI(MPI_Comm_size(comm, &size));
1536     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1537     PetscCall(DMGetDimension(dm, &dim));
1538     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1539     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1540     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1541     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1542     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1543     PetscCall(DMPlexGetDepth(dm, &locDepth));
1544     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1545     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1546     gcNum = gcEnd - gcStart;
1547     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1548     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1549     for (d = 0; d <= depth; d++) {
1550       PetscInt Nc[2] = {0, 0}, ict;
1551 
1552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1553       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1554       ict = ct0;
1555       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1556       ct0 = (DMPolytopeType)ict;
1557       for (p = pStart; p < pEnd; ++p) {
1558         DMPolytopeType ct;
1559 
1560         PetscCall(DMPlexGetCellType(dm, p, &ct));
1561         if (ct == ct0) ++Nc[0];
1562         else ++Nc[1];
1563       }
1564       if (size < maxSize) {
1565         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1566         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1567         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1568         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1569         for (p = 0; p < size; ++p) {
1570           if (rank == 0) {
1571             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1572             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1573             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1574           }
1575         }
1576       } else {
1577         PetscInt locMinMax[2];
1578 
1579         locMinMax[0] = Nc[0] + Nc[1];
1580         locMinMax[1] = Nc[0] + Nc[1];
1581         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1582         locMinMax[0] = Nc[1];
1583         locMinMax[1] = Nc[1];
1584         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1585         if (d == depth) {
1586           locMinMax[0] = gcNum;
1587           locMinMax[1] = gcNum;
1588           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1589         }
1590         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1591         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1592         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1593         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1594       }
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1596     }
1597     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1598     {
1599       const PetscReal *maxCell;
1600       const PetscReal *L;
1601       PetscBool        localized;
1602 
1603       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1604       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1605       if (L || localized) {
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1607         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1608         if (L) {
1609           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1610           for (d = 0; d < dim; ++d) {
1611             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1612             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1613           }
1614           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1615         }
1616         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1617         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1618       }
1619     }
1620     PetscCall(DMGetNumLabels(dm, &numLabels));
1621     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1622     for (l = 0; l < numLabels; ++l) {
1623       DMLabel         label;
1624       const char     *name;
1625       IS              valueIS;
1626       const PetscInt *values;
1627       PetscInt        numValues, v;
1628 
1629       PetscCall(DMGetLabelName(dm, l, &name));
1630       PetscCall(DMGetLabel(dm, name, &label));
1631       PetscCall(DMLabelGetNumValues(label, &numValues));
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1633       PetscCall(DMLabelGetValueIS(label, &valueIS));
1634       PetscCall(ISGetIndices(valueIS, &values));
1635       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1636       for (v = 0; v < numValues; ++v) {
1637         PetscInt size;
1638 
1639         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1640         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1642       }
1643       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1644       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1645       PetscCall(ISRestoreIndices(valueIS, &values));
1646       PetscCall(ISDestroy(&valueIS));
1647     }
1648     {
1649       char    **labelNames;
1650       PetscInt  Nl = numLabels;
1651       PetscBool flg;
1652 
1653       PetscCall(PetscMalloc1(Nl, &labelNames));
1654       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1655       for (l = 0; l < Nl; ++l) {
1656         DMLabel label;
1657 
1658         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1659         if (flg) {
1660           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1661           PetscCall(DMLabelView(label, viewer));
1662         }
1663         PetscCall(PetscFree(labelNames[l]));
1664       }
1665       PetscCall(PetscFree(labelNames));
1666     }
1667     /* If no fields are specified, people do not want to see adjacency */
1668     if (dm->Nf) {
1669       PetscInt f;
1670 
1671       for (f = 0; f < dm->Nf; ++f) {
1672         const char *name;
1673 
1674         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1675         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1676         PetscCall(PetscViewerASCIIPushTab(viewer));
1677         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1678         if (dm->fields[f].adjacency[0]) {
1679           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1680           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1681         } else {
1682           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1683           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1684         }
1685         PetscCall(PetscViewerASCIIPopTab(viewer));
1686       }
1687     }
1688     PetscCall(DMGetCoarseDM(dm, &cdm));
1689     if (cdm) {
1690       PetscCall(PetscViewerASCIIPushTab(viewer));
1691       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1692       PetscCall(DMPlexView_Ascii(cdm, viewer));
1693       PetscCall(PetscViewerASCIIPopTab(viewer));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1700 {
1701   DMPolytopeType ct;
1702   PetscMPIInt    rank;
1703   PetscInt       cdim;
1704 
1705   PetscFunctionBegin;
1706   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1707   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1708   PetscCall(DMGetCoordinateDim(dm, &cdim));
1709   switch (ct) {
1710   case DM_POLYTOPE_SEGMENT:
1711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1712     switch (cdim) {
1713     case 1: {
1714       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1715       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1716 
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1719       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1720     } break;
1721     case 2: {
1722       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1723       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1724       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1725 
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1727       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1728       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1729     } break;
1730     default:
1731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1732     }
1733     break;
1734   case DM_POLYTOPE_TRIANGLE:
1735     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1738     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1739     break;
1740   case DM_POLYTOPE_QUADRILATERAL:
1741     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1742     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1746     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1747     break;
1748   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1749     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1750     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_FV_GHOST:
1757     break;
1758   default:
1759     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1760   }
1761   PetscFunctionReturn(PETSC_SUCCESS);
1762 }
1763 
1764 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1765 {
1766   PetscReal   centroid[2] = {0., 0.};
1767   PetscMPIInt rank;
1768   PetscInt    fillColor;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1773   for (PetscInt v = 0; v < Nv; ++v) {
1774     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1775     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1776   }
1777   for (PetscInt e = 0; e < Nv; ++e) {
1778     refCoords[0] = refVertices[e * 2 + 0];
1779     refCoords[1] = refVertices[e * 2 + 1];
1780     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1781       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1782       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1783     }
1784     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1785     for (PetscInt d = 0; d < edgeDiv; ++d) {
1786       PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1787       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1788     }
1789   }
1790   PetscFunctionReturn(PETSC_SUCCESS);
1791 }
1792 
1793 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1794 {
1795   DMPolytopeType ct;
1796 
1797   PetscFunctionBegin;
1798   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1799   switch (ct) {
1800   case DM_POLYTOPE_TRIANGLE: {
1801     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1802 
1803     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1804   } break;
1805   case DM_POLYTOPE_QUADRILATERAL: {
1806     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1807 
1808     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1809   } break;
1810   default:
1811     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1812   }
1813   PetscFunctionReturn(PETSC_SUCCESS);
1814 }
1815 
1816 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1817 {
1818   PetscDraw    draw;
1819   DM           cdm;
1820   PetscSection coordSection;
1821   Vec          coordinates;
1822   PetscReal    xyl[3], xyr[3];
1823   PetscReal   *refCoords, *edgeCoords;
1824   PetscBool    isnull, drawAffine;
1825   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1826 
1827   PetscFunctionBegin;
1828   PetscCall(DMGetCoordinateDim(dm, &dim));
1829   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1830   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1831   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1832   edgeDiv    = cDegree + 1;
1833   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1834   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1835   PetscCall(DMGetCoordinateDM(dm, &cdm));
1836   PetscCall(DMGetLocalSection(cdm, &coordSection));
1837   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1838   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1839   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1840 
1841   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1842   PetscCall(PetscDrawIsNull(draw, &isnull));
1843   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1844   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1845 
1846   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1847   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1848   PetscCall(PetscDrawClear(draw));
1849 
1850   for (c = cStart; c < cEnd; ++c) {
1851     PetscScalar       *coords = NULL;
1852     const PetscScalar *coords_arr;
1853     PetscInt           numCoords;
1854     PetscBool          isDG;
1855 
1856     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1857     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1858     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1859     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1860   }
1861   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1862   PetscCall(PetscDrawFlush(draw));
1863   PetscCall(PetscDrawPause(draw));
1864   PetscCall(PetscDrawSave(draw));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1869 {
1870   DM           odm = dm, rdm = dm, cdm;
1871   PetscFE      fe;
1872   PetscSpace   sp;
1873   PetscClassId id;
1874   PetscInt     degree;
1875   PetscBool    hoView = PETSC_TRUE;
1876 
1877   PetscFunctionBegin;
1878   PetscObjectOptionsBegin((PetscObject)dm);
1879   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1880   PetscOptionsEnd();
1881   PetscCall(PetscObjectReference((PetscObject)dm));
1882   *hdm = dm;
1883   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1884   PetscCall(DMGetCoordinateDM(dm, &cdm));
1885   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1886   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1887   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1888   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1889   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1890   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1891     DM  cdm, rcdm;
1892     Mat In;
1893     Vec cl, rcl;
1894 
1895     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1896     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1897     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1898     PetscCall(DMGetCoordinateDM(odm, &cdm));
1899     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1900     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1901     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1902     PetscCall(DMSetCoarseDM(rcdm, cdm));
1903     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1904     PetscCall(MatMult(In, cl, rcl));
1905     PetscCall(MatDestroy(&In));
1906     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1907     PetscCall(DMDestroy(&odm));
1908     odm = rdm;
1909   }
1910   *hdm = rdm;
1911   PetscFunctionReturn(PETSC_SUCCESS);
1912 }
1913 
1914 #if defined(PETSC_HAVE_EXODUSII)
1915   #include <exodusII.h>
1916   #include <petscviewerexodusii.h>
1917 #endif
1918 
1919 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1920 {
1921   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1922   char      name[PETSC_MAX_PATH_LEN];
1923 
1924   PetscFunctionBegin;
1925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1926   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1932   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1933   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1934   if (iascii) {
1935     PetscViewerFormat format;
1936     PetscCall(PetscViewerGetFormat(viewer, &format));
1937     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1938     else PetscCall(DMPlexView_Ascii(dm, viewer));
1939   } else if (ishdf5) {
1940 #if defined(PETSC_HAVE_HDF5)
1941     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1942 #else
1943     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1944 #endif
1945   } else if (isvtk) {
1946     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1947   } else if (isdraw) {
1948     DM hdm;
1949 
1950     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1951     PetscCall(DMPlexView_Draw(hdm, viewer));
1952     PetscCall(DMDestroy(&hdm));
1953   } else if (isglvis) {
1954     PetscCall(DMPlexView_GLVis(dm, viewer));
1955 #if defined(PETSC_HAVE_EXODUSII)
1956   } else if (isexodus) {
1957     /*
1958       exodusII requires that all sets be part of exactly one cell set.
1959       If the dm does not have a "Cell Sets" label defined, we create one
1960       with ID 1, containing all cells.
1961       Note that if the Cell Sets label is defined but does not cover all cells,
1962       we may still have a problem. This should probably be checked here or in the viewer;
1963     */
1964     PetscInt numCS;
1965     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1966     if (!numCS) {
1967       PetscInt cStart, cEnd, c;
1968       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1969       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1970       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1971     }
1972     PetscCall(DMView_PlexExodusII(dm, viewer));
1973 #endif
1974 #if defined(PETSC_HAVE_CGNS)
1975   } else if (iscgns) {
1976     PetscCall(DMView_PlexCGNS(dm, viewer));
1977 #endif
1978   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1979   /* Optionally view the partition */
1980   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1981   if (flg) {
1982     Vec ranks;
1983     PetscCall(DMPlexCreateRankField(dm, &ranks));
1984     PetscCall(VecView(ranks, viewer));
1985     PetscCall(VecDestroy(&ranks));
1986   }
1987   /* Optionally view a label */
1988   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1989   if (flg) {
1990     DMLabel label;
1991     Vec     val;
1992 
1993     PetscCall(DMGetLabel(dm, name, &label));
1994     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1995     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1996     PetscCall(VecView(val, viewer));
1997     PetscCall(VecDestroy(&val));
1998   }
1999   PetscFunctionReturn(PETSC_SUCCESS);
2000 }
2001 
2002 /*@
2003   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2004 
2005   Collective
2006 
2007   Input Parameters:
2008 + dm     - The `DM` whose topology is to be saved
2009 - viewer - The `PetscViewer` to save it in
2010 
2011   Level: advanced
2012 
2013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2014 @*/
2015 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2016 {
2017   PetscBool ishdf5;
2018 
2019   PetscFunctionBegin;
2020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2021   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2022   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2023   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2024   if (ishdf5) {
2025 #if defined(PETSC_HAVE_HDF5)
2026     PetscViewerFormat format;
2027     PetscCall(PetscViewerGetFormat(viewer, &format));
2028     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2029       IS globalPointNumbering;
2030 
2031       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2032       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2033       PetscCall(ISDestroy(&globalPointNumbering));
2034     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2035 #else
2036     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2037 #endif
2038   }
2039   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2040   PetscFunctionReturn(PETSC_SUCCESS);
2041 }
2042 
2043 /*@
2044   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2045 
2046   Collective
2047 
2048   Input Parameters:
2049 + dm     - The `DM` whose coordinates are to be saved
2050 - viewer - The `PetscViewer` for saving
2051 
2052   Level: advanced
2053 
2054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2055 @*/
2056 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2057 {
2058   PetscBool ishdf5;
2059 
2060   PetscFunctionBegin;
2061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2062   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2064   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2065   if (ishdf5) {
2066 #if defined(PETSC_HAVE_HDF5)
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2070       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2071     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   }
2076   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2077   PetscFunctionReturn(PETSC_SUCCESS);
2078 }
2079 
2080 /*@
2081   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2082 
2083   Collective
2084 
2085   Input Parameters:
2086 + dm     - The `DM` whose labels are to be saved
2087 - viewer - The `PetscViewer` for saving
2088 
2089   Level: advanced
2090 
2091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2092 @*/
2093 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2094 {
2095   PetscBool ishdf5;
2096 
2097   PetscFunctionBegin;
2098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2099   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2100   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2101   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2102   if (ishdf5) {
2103 #if defined(PETSC_HAVE_HDF5)
2104     IS                globalPointNumbering;
2105     PetscViewerFormat format;
2106 
2107     PetscCall(PetscViewerGetFormat(viewer, &format));
2108     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2109       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2110       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2111       PetscCall(ISDestroy(&globalPointNumbering));
2112     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2113 #else
2114     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2115 #endif
2116   }
2117   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2118   PetscFunctionReturn(PETSC_SUCCESS);
2119 }
2120 
2121 /*@
2122   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2123 
2124   Collective
2125 
2126   Input Parameters:
2127 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2128 . viewer    - The `PetscViewer` for saving
2129 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2130 
2131   Level: advanced
2132 
2133   Notes:
2134   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (`dm`) points. When the topology (`dm`) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
2135 
2136   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2137 
2138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2139 @*/
2140 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2141 {
2142   PetscBool ishdf5;
2143 
2144   PetscFunctionBegin;
2145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2146   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2147   if (!sectiondm) sectiondm = dm;
2148   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2149   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2150   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2151   if (ishdf5) {
2152 #if defined(PETSC_HAVE_HDF5)
2153     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2154 #else
2155     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2156 #endif
2157   }
2158   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2159   PetscFunctionReturn(PETSC_SUCCESS);
2160 }
2161 
2162 /*@
2163   DMPlexGlobalVectorView - Saves a global vector
2164 
2165   Collective
2166 
2167   Input Parameters:
2168 + dm        - The `DM` that represents the topology
2169 . viewer    - The `PetscViewer` to save data with
2170 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2171 - vec       - The global vector to be saved
2172 
2173   Level: advanced
2174 
2175   Notes:
2176   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2177 
2178   Calling sequence:
2179 .vb
2180        DMCreate(PETSC_COMM_WORLD, &dm);
2181        DMSetType(dm, DMPLEX);
2182        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2183        DMClone(dm, &sectiondm);
2184        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2185        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2186        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2187        PetscSectionSetChart(section, pStart, pEnd);
2188        PetscSectionSetUp(section);
2189        DMSetLocalSection(sectiondm, section);
2190        PetscSectionDestroy(&section);
2191        DMGetGlobalVector(sectiondm, &vec);
2192        PetscObjectSetName((PetscObject)vec, "vec_name");
2193        DMPlexTopologyView(dm, viewer);
2194        DMPlexSectionView(dm, viewer, sectiondm);
2195        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2196        DMRestoreGlobalVector(sectiondm, &vec);
2197        DMDestroy(&sectiondm);
2198        DMDestroy(&dm);
2199 .ve
2200 
2201 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2202 @*/
2203 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2204 {
2205   PetscBool ishdf5;
2206 
2207   PetscFunctionBegin;
2208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2209   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2210   if (!sectiondm) sectiondm = dm;
2211   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2212   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2213   /* Check consistency */
2214   {
2215     PetscSection section;
2216     PetscBool    includesConstraints;
2217     PetscInt     m, m1;
2218 
2219     PetscCall(VecGetLocalSize(vec, &m1));
2220     PetscCall(DMGetGlobalSection(sectiondm, &section));
2221     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2222     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2223     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2224     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2225   }
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2231 #else
2232     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2233 #endif
2234   }
2235   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2236   PetscFunctionReturn(PETSC_SUCCESS);
2237 }
2238 
2239 /*@
2240   DMPlexLocalVectorView - Saves a local vector
2241 
2242   Collective
2243 
2244   Input Parameters:
2245 + dm        - The `DM` that represents the topology
2246 . viewer    - The `PetscViewer` to save data with
2247 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2248 - vec       - The local vector to be saved
2249 
2250   Level: advanced
2251 
2252   Note:
2253   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2254 
2255   Calling sequence:
2256 .vb
2257        DMCreate(PETSC_COMM_WORLD, &dm);
2258        DMSetType(dm, DMPLEX);
2259        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2260        DMClone(dm, &sectiondm);
2261        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2262        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2263        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2264        PetscSectionSetChart(section, pStart, pEnd);
2265        PetscSectionSetUp(section);
2266        DMSetLocalSection(sectiondm, section);
2267        DMGetLocalVector(sectiondm, &vec);
2268        PetscObjectSetName((PetscObject)vec, "vec_name");
2269        DMPlexTopologyView(dm, viewer);
2270        DMPlexSectionView(dm, viewer, sectiondm);
2271        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2272        DMRestoreLocalVector(sectiondm, &vec);
2273        DMDestroy(&sectiondm);
2274        DMDestroy(&dm);
2275 .ve
2276 
2277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2278 @*/
2279 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2280 {
2281   PetscBool ishdf5;
2282 
2283   PetscFunctionBegin;
2284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2285   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2286   if (!sectiondm) sectiondm = dm;
2287   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2288   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2289   /* Check consistency */
2290   {
2291     PetscSection section;
2292     PetscBool    includesConstraints;
2293     PetscInt     m, m1;
2294 
2295     PetscCall(VecGetLocalSize(vec, &m1));
2296     PetscCall(DMGetLocalSection(sectiondm, &section));
2297     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2298     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2299     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2300     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2301   }
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2304   if (ishdf5) {
2305 #if defined(PETSC_HAVE_HDF5)
2306     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2307 #else
2308     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2309 #endif
2310   }
2311   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2312   PetscFunctionReturn(PETSC_SUCCESS);
2313 }
2314 
2315 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2316 {
2317   PetscBool ishdf5;
2318 
2319   PetscFunctionBegin;
2320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2321   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2322   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2323   if (ishdf5) {
2324 #if defined(PETSC_HAVE_HDF5)
2325     PetscViewerFormat format;
2326     PetscCall(PetscViewerGetFormat(viewer, &format));
2327     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2328       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2329     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2330       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2331     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2332     PetscFunctionReturn(PETSC_SUCCESS);
2333 #else
2334     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2335 #endif
2336   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2337 }
2338 
2339 /*@
2340   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2341 
2342   Collective
2343 
2344   Input Parameters:
2345 + dm     - The `DM` into which the topology is loaded
2346 - viewer - The `PetscViewer` for the saved topology
2347 
2348   Output Parameter:
2349 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2350 
2351   Level: advanced
2352 
2353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2354           `PetscViewer`, `PetscSF`
2355 @*/
2356 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2357 {
2358   PetscBool ishdf5;
2359 
2360   PetscFunctionBegin;
2361   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2362   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2363   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2364   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2365   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2366   if (ishdf5) {
2367 #if defined(PETSC_HAVE_HDF5)
2368     PetscViewerFormat format;
2369     PetscCall(PetscViewerGetFormat(viewer, &format));
2370     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2371       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2372     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2373 #else
2374     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2375 #endif
2376   }
2377   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2378   PetscFunctionReturn(PETSC_SUCCESS);
2379 }
2380 
2381 /*@
2382   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2383 
2384   Collective
2385 
2386   Input Parameters:
2387 + dm                   - The `DM` into which the coordinates are loaded
2388 . viewer               - The `PetscViewer` for the saved coordinates
2389 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2390 
2391   Level: advanced
2392 
2393 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2394           `PetscSF`, `PetscViewer`
2395 @*/
2396 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2397 {
2398   PetscBool ishdf5;
2399 
2400   PetscFunctionBegin;
2401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2402   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2403   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2404   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2405   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2406   if (ishdf5) {
2407 #if defined(PETSC_HAVE_HDF5)
2408     PetscViewerFormat format;
2409     PetscCall(PetscViewerGetFormat(viewer, &format));
2410     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2411       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2412     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2413 #else
2414     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2415 #endif
2416   }
2417   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2418   PetscFunctionReturn(PETSC_SUCCESS);
2419 }
2420 
2421 /*@
2422   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2423 
2424   Collective
2425 
2426   Input Parameters:
2427 + dm                   - The `DM` into which the labels are loaded
2428 . viewer               - The `PetscViewer` for the saved labels
2429 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2430 
2431   Level: advanced
2432 
2433   Note:
2434   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2435 
2436 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2437           `PetscSF`, `PetscViewer`
2438 @*/
2439 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2440 {
2441   PetscBool ishdf5;
2442 
2443   PetscFunctionBegin;
2444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2445   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2446   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2447   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2448   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2449   if (ishdf5) {
2450 #if defined(PETSC_HAVE_HDF5)
2451     PetscViewerFormat format;
2452 
2453     PetscCall(PetscViewerGetFormat(viewer, &format));
2454     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2455       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2456     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2457 #else
2458     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2459 #endif
2460   }
2461   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2462   PetscFunctionReturn(PETSC_SUCCESS);
2463 }
2464 
2465 /*@
2466   DMPlexSectionLoad - Loads section into a `DMPLEX`
2467 
2468   Collective
2469 
2470   Input Parameters:
2471 + dm                   - The `DM` that represents the topology
2472 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2473 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2474 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2475 
2476   Output Parameters:
2477 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2478 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2479 
2480   Level: advanced
2481 
2482   Notes:
2483   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2484 
2485   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2486 
2487   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2488 
2489   Example using 2 processes:
2490 .vb
2491   NX (number of points on dm): 4
2492   sectionA                   : the on-disk section
2493   vecA                       : a vector associated with sectionA
2494   sectionB                   : sectiondm's local section constructed in this function
2495   vecB (local)               : a vector associated with sectiondm's local section
2496   vecB (global)              : a vector associated with sectiondm's global section
2497 
2498                                      rank 0    rank 1
2499   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2500   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2501   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2502   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2503   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2504   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2505   sectionB->atlasDof             :     1 0 1 | 1 3
2506   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2507   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2508   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2509 .ve
2510   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2511 
2512 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2513 @*/
2514 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2515 {
2516   PetscBool ishdf5;
2517 
2518   PetscFunctionBegin;
2519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2520   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2521   if (!sectiondm) sectiondm = dm;
2522   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2523   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2524   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2525   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2527   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2528   if (ishdf5) {
2529 #if defined(PETSC_HAVE_HDF5)
2530     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2531 #else
2532     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2533 #endif
2534   }
2535   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2536   PetscFunctionReturn(PETSC_SUCCESS);
2537 }
2538 
2539 /*@
2540   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2541 
2542   Collective
2543 
2544   Input Parameters:
2545 + dm        - The `DM` that represents the topology
2546 . viewer    - The `PetscViewer` that represents the on-disk vector data
2547 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2548 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2549 - vec       - The global vector to set values of
2550 
2551   Level: advanced
2552 
2553   Notes:
2554   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2555 
2556   Calling sequence:
2557 .vb
2558        DMCreate(PETSC_COMM_WORLD, &dm);
2559        DMSetType(dm, DMPLEX);
2560        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2561        DMPlexTopologyLoad(dm, viewer, &sfX);
2562        DMClone(dm, &sectiondm);
2563        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2564        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2565        DMGetGlobalVector(sectiondm, &vec);
2566        PetscObjectSetName((PetscObject)vec, "vec_name");
2567        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2568        DMRestoreGlobalVector(sectiondm, &vec);
2569        PetscSFDestroy(&gsf);
2570        PetscSFDestroy(&sfX);
2571        DMDestroy(&sectiondm);
2572        DMDestroy(&dm);
2573 .ve
2574 
2575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2576           `PetscSF`, `PetscViewer`
2577 @*/
2578 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2579 {
2580   PetscBool ishdf5;
2581 
2582   PetscFunctionBegin;
2583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2584   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2585   if (!sectiondm) sectiondm = dm;
2586   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2587   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2588   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2589   /* Check consistency */
2590   {
2591     PetscSection section;
2592     PetscBool    includesConstraints;
2593     PetscInt     m, m1;
2594 
2595     PetscCall(VecGetLocalSize(vec, &m1));
2596     PetscCall(DMGetGlobalSection(sectiondm, &section));
2597     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2598     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2599     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2600     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2601   }
2602   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2603   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2604   if (ishdf5) {
2605 #if defined(PETSC_HAVE_HDF5)
2606     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2607 #else
2608     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2609 #endif
2610   }
2611   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2612   PetscFunctionReturn(PETSC_SUCCESS);
2613 }
2614 
2615 /*@
2616   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2617 
2618   Collective
2619 
2620   Input Parameters:
2621 + dm        - The `DM` that represents the topology
2622 . viewer    - The `PetscViewer` that represents the on-disk vector data
2623 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2624 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2625 - vec       - The local vector to set values of
2626 
2627   Level: advanced
2628 
2629   Notes:
2630   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2631 
2632   Calling sequence:
2633 .vb
2634        DMCreate(PETSC_COMM_WORLD, &dm);
2635        DMSetType(dm, DMPLEX);
2636        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2637        DMPlexTopologyLoad(dm, viewer, &sfX);
2638        DMClone(dm, &sectiondm);
2639        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2640        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2641        DMGetLocalVector(sectiondm, &vec);
2642        PetscObjectSetName((PetscObject)vec, "vec_name");
2643        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2644        DMRestoreLocalVector(sectiondm, &vec);
2645        PetscSFDestroy(&lsf);
2646        PetscSFDestroy(&sfX);
2647        DMDestroy(&sectiondm);
2648        DMDestroy(&dm);
2649 .ve
2650 
2651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2652           `PetscSF`, `PetscViewer`
2653 @*/
2654 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2655 {
2656   PetscBool ishdf5;
2657 
2658   PetscFunctionBegin;
2659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2660   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2661   if (!sectiondm) sectiondm = dm;
2662   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2663   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2664   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2665   /* Check consistency */
2666   {
2667     PetscSection section;
2668     PetscBool    includesConstraints;
2669     PetscInt     m, m1;
2670 
2671     PetscCall(VecGetLocalSize(vec, &m1));
2672     PetscCall(DMGetLocalSection(sectiondm, &section));
2673     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2674     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2675     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2676     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2677   }
2678   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2679   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2680   if (ishdf5) {
2681 #if defined(PETSC_HAVE_HDF5)
2682     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2683 #else
2684     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2685 #endif
2686   }
2687   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2688   PetscFunctionReturn(PETSC_SUCCESS);
2689 }
2690 
2691 PetscErrorCode DMDestroy_Plex(DM dm)
2692 {
2693   DM_Plex *mesh = (DM_Plex *)dm->data;
2694 
2695   PetscFunctionBegin;
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2714   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2716   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2717   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2718   PetscCall(PetscFree(mesh->cones));
2719   PetscCall(PetscFree(mesh->coneOrientations));
2720   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2721   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2722   PetscCall(PetscFree(mesh->supports));
2723   PetscCall(PetscFree(mesh->cellTypes));
2724   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2725   PetscCall(PetscFree(mesh->tetgenOpts));
2726   PetscCall(PetscFree(mesh->triangleOpts));
2727   PetscCall(PetscFree(mesh->transformType));
2728   PetscCall(PetscFree(mesh->distributionName));
2729   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2730   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2731   PetscCall(ISDestroy(&mesh->subpointIS));
2732   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2733   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2734   if (mesh->periodic.face_sfs) {
2735     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2736     PetscCall(PetscFree(mesh->periodic.face_sfs));
2737   }
2738   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2739   if (mesh->periodic.periodic_points) {
2740     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2741     PetscCall(PetscFree(mesh->periodic.periodic_points));
2742   }
2743   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2744   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2745   PetscCall(ISDestroy(&mesh->anchorIS));
2746   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2747   PetscCall(PetscFree(mesh->parents));
2748   PetscCall(PetscFree(mesh->childIDs));
2749   PetscCall(PetscSectionDestroy(&mesh->childSection));
2750   PetscCall(PetscFree(mesh->children));
2751   PetscCall(DMDestroy(&mesh->referenceTree));
2752   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2753   PetscCall(PetscFree(mesh->neighbors));
2754   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2755   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2756   PetscCall(PetscFree(mesh));
2757   PetscFunctionReturn(PETSC_SUCCESS);
2758 }
2759 
2760 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2761 {
2762   PetscSection           sectionGlobal, sectionLocal;
2763   PetscInt               bs = -1, mbs;
2764   PetscInt               localSize, localStart = 0;
2765   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2766   MatType                mtype;
2767   ISLocalToGlobalMapping ltog;
2768 
2769   PetscFunctionBegin;
2770   PetscCall(MatInitializePackage());
2771   mtype = dm->mattype;
2772   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2773   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2774   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2775   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2776   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2777   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2778   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2779   PetscCall(MatSetType(*J, mtype));
2780   PetscCall(MatSetFromOptions(*J));
2781   PetscCall(MatGetBlockSize(*J, &mbs));
2782   if (mbs > 1) bs = mbs;
2783   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2784   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2785   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2786   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2787   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2788   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2789   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2790   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2791   if (!isShell) {
2792     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2793     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2794     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2795 
2796     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2797 
2798     PetscCall(PetscCalloc1(localSize, &pblocks));
2799     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2800     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2801     for (p = pStart; p < pEnd; ++p) {
2802       switch (dm->blocking_type) {
2803       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2804         PetscInt bdof, offset;
2805 
2806         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2807         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2808         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2809         if (dof > 0) {
2810           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2811           // Signal block concatenation
2812           if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2813         }
2814         dof  = dof < 0 ? -(dof + 1) : dof;
2815         bdof = cdof && (dof - cdof) ? 1 : dof;
2816         if (dof) {
2817           if (bs < 0) {
2818             bs = bdof;
2819           } else if (bs != bdof) {
2820             bs = 1;
2821           }
2822         }
2823       } break;
2824       case DM_BLOCKING_FIELD_NODE: {
2825         for (PetscInt field = 0; field < num_fields; field++) {
2826           PetscInt num_comp, bdof, offset;
2827           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2828           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2829           if (dof < 0) continue;
2830           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2831           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2832           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2833           PetscInt num_nodes = dof / num_comp;
2834           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2835           // Handle possibly constant block size (unlikely)
2836           bdof = cdof && (dof - cdof) ? 1 : dof;
2837           if (dof) {
2838             if (bs < 0) {
2839               bs = bdof;
2840             } else if (bs != bdof) {
2841               bs = 1;
2842             }
2843           }
2844         }
2845       } break;
2846       }
2847     }
2848     /* Must have same blocksize on all procs (some might have no points) */
2849     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2850     bsLocal[1] = bs;
2851     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2852     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2853     else bs = bsMinMax[0];
2854     bs = PetscMax(1, bs);
2855     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2856     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2857       PetscCall(MatSetBlockSize(*J, bs));
2858       PetscCall(MatSetUp(*J));
2859     } else {
2860       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2861       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2862       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2863     }
2864     if (pblocks) { // Consolidate blocks
2865       PetscInt nblocks = 0;
2866       pblocks[0]       = PetscAbs(pblocks[0]);
2867       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2868         if (pblocks[i] == 0) continue;
2869         // Negative block size indicates the blocks should be concatenated
2870         if (pblocks[i] < 0) {
2871           pblocks[i] = -pblocks[i];
2872           pblocks[nblocks - 1] += pblocks[i];
2873         } else {
2874           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2875         }
2876         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2877       }
2878       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2879     }
2880     PetscCall(PetscFree(pblocks));
2881   }
2882   PetscCall(MatSetDM(*J, dm));
2883   PetscFunctionReturn(PETSC_SUCCESS);
2884 }
2885 
2886 /*@
2887   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2888 
2889   Not Collective
2890 
2891   Input Parameter:
2892 . dm - The `DMPLEX`
2893 
2894   Output Parameter:
2895 . subsection - The subdomain section
2896 
2897   Level: developer
2898 
2899 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2900 @*/
2901 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2902 {
2903   DM_Plex *mesh = (DM_Plex *)dm->data;
2904 
2905   PetscFunctionBegin;
2906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2907   if (!mesh->subdomainSection) {
2908     PetscSection section;
2909     PetscSF      sf;
2910 
2911     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2912     PetscCall(DMGetLocalSection(dm, &section));
2913     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2914     PetscCall(PetscSFDestroy(&sf));
2915   }
2916   *subsection = mesh->subdomainSection;
2917   PetscFunctionReturn(PETSC_SUCCESS);
2918 }
2919 
2920 /*@
2921   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2922 
2923   Not Collective
2924 
2925   Input Parameter:
2926 . dm - The `DMPLEX`
2927 
2928   Output Parameters:
2929 + pStart - The first mesh point
2930 - pEnd   - The upper bound for mesh points
2931 
2932   Level: beginner
2933 
2934 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2935 @*/
2936 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2937 {
2938   DM_Plex *mesh = (DM_Plex *)dm->data;
2939 
2940   PetscFunctionBegin;
2941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2942   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2943   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2944   PetscFunctionReturn(PETSC_SUCCESS);
2945 }
2946 
2947 /*@
2948   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2949 
2950   Not Collective
2951 
2952   Input Parameters:
2953 + dm     - The `DMPLEX`
2954 . pStart - The first mesh point
2955 - pEnd   - The upper bound for mesh points
2956 
2957   Level: beginner
2958 
2959 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2960 @*/
2961 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2962 {
2963   DM_Plex *mesh = (DM_Plex *)dm->data;
2964 
2965   PetscFunctionBegin;
2966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2967   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2968   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2969   PetscCall(PetscFree(mesh->cellTypes));
2970   PetscFunctionReturn(PETSC_SUCCESS);
2971 }
2972 
2973 /*@
2974   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2975 
2976   Not Collective
2977 
2978   Input Parameters:
2979 + dm - The `DMPLEX`
2980 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2981 
2982   Output Parameter:
2983 . size - The cone size for point `p`
2984 
2985   Level: beginner
2986 
2987 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2988 @*/
2989 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2990 {
2991   DM_Plex *mesh = (DM_Plex *)dm->data;
2992 
2993   PetscFunctionBegin;
2994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2995   PetscAssertPointer(size, 3);
2996   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2997   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2998   PetscFunctionReturn(PETSC_SUCCESS);
2999 }
3000 
3001 /*@
3002   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3003 
3004   Not Collective
3005 
3006   Input Parameters:
3007 + dm   - The `DMPLEX`
3008 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3009 - size - The cone size for point `p`
3010 
3011   Level: beginner
3012 
3013   Note:
3014   This should be called after `DMPlexSetChart()`.
3015 
3016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3017 @*/
3018 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3019 {
3020   DM_Plex *mesh = (DM_Plex *)dm->data;
3021 
3022   PetscFunctionBegin;
3023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3024   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3025   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3026   PetscFunctionReturn(PETSC_SUCCESS);
3027 }
3028 
3029 /*@C
3030   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3031 
3032   Not Collective
3033 
3034   Input Parameters:
3035 + dm - The `DMPLEX`
3036 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3037 
3038   Output Parameter:
3039 . cone - An array of points which are on the in-edges for point `p`
3040 
3041   Level: beginner
3042 
3043   Fortran Notes:
3044   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3045   `DMPlexRestoreCone()` is not needed/available in C.
3046 
3047 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3048 @*/
3049 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3050 {
3051   DM_Plex *mesh = (DM_Plex *)dm->data;
3052   PetscInt off;
3053 
3054   PetscFunctionBegin;
3055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3056   PetscAssertPointer(cone, 3);
3057   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3058   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3059   PetscFunctionReturn(PETSC_SUCCESS);
3060 }
3061 
3062 /*@
3063   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3064 
3065   Not Collective
3066 
3067   Input Parameters:
3068 + dm - The `DMPLEX`
3069 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3070 
3071   Output Parameters:
3072 + pConesSection - `PetscSection` describing the layout of `pCones`
3073 - pCones        - An array of points which are on the in-edges for the point set `p`
3074 
3075   Level: intermediate
3076 
3077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3078 @*/
3079 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3080 {
3081   PetscSection cs, newcs;
3082   PetscInt    *cones;
3083   PetscInt    *newarr = NULL;
3084   PetscInt     n;
3085 
3086   PetscFunctionBegin;
3087   PetscCall(DMPlexGetCones(dm, &cones));
3088   PetscCall(DMPlexGetConeSection(dm, &cs));
3089   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3090   if (pConesSection) *pConesSection = newcs;
3091   if (pCones) {
3092     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3093     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3094   }
3095   PetscFunctionReturn(PETSC_SUCCESS);
3096 }
3097 
3098 /*@
3099   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3100 
3101   Not Collective
3102 
3103   Input Parameters:
3104 + dm     - The `DMPLEX`
3105 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3106 
3107   Output Parameter:
3108 . expandedPoints - An array of vertices recursively expanded from input points
3109 
3110   Level: advanced
3111 
3112   Notes:
3113   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3114 
3115   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3116 
3117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3118           `DMPlexGetDepth()`, `IS`
3119 @*/
3120 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3121 {
3122   IS      *expandedPointsAll;
3123   PetscInt depth;
3124 
3125   PetscFunctionBegin;
3126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3127   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3128   PetscAssertPointer(expandedPoints, 3);
3129   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3130   *expandedPoints = expandedPointsAll[0];
3131   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3132   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3133   PetscFunctionReturn(PETSC_SUCCESS);
3134 }
3135 
3136 /*@
3137   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
3138 
3139   Not Collective
3140 
3141   Input Parameters:
3142 + dm     - The `DMPLEX`
3143 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3144 
3145   Output Parameters:
3146 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3147 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3148 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3149 
3150   Level: advanced
3151 
3152   Notes:
3153   Like `DMPlexGetConeTuple()` but recursive.
3154 
3155   Array `expandedPoints` has size equal to `depth`. Each `expandedPoints`[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
3156   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3157 
3158   Array section has size equal to `depth`.  Each `PetscSection` `sections`[d] realizes mapping from `expandedPoints`[d+1] (section points) to `expandedPoints`[d] (section dofs) as follows\:
3159   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3160   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3161 
3162 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3163           `DMPlexGetDepth()`, `PetscSection`, `IS`
3164 @*/
3165 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3166 {
3167   const PetscInt *arr0 = NULL, *cone = NULL;
3168   PetscInt       *arr = NULL, *newarr = NULL;
3169   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3170   IS             *expandedPoints_;
3171   PetscSection   *sections_;
3172 
3173   PetscFunctionBegin;
3174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3175   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3176   if (depth) PetscAssertPointer(depth, 3);
3177   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3178   if (sections) PetscAssertPointer(sections, 5);
3179   PetscCall(ISGetLocalSize(points, &n));
3180   PetscCall(ISGetIndices(points, &arr0));
3181   PetscCall(DMPlexGetDepth(dm, &depth_));
3182   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3183   PetscCall(PetscCalloc1(depth_, &sections_));
3184   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3185   for (d = depth_ - 1; d >= 0; d--) {
3186     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3187     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3188     for (i = 0; i < n; i++) {
3189       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3190       if (arr[i] >= start && arr[i] < end) {
3191         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3192         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3193       } else {
3194         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3195       }
3196     }
3197     PetscCall(PetscSectionSetUp(sections_[d]));
3198     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3199     PetscCall(PetscMalloc1(newn, &newarr));
3200     for (i = 0; i < n; i++) {
3201       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3202       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3203       if (cn > 1) {
3204         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3205         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3206       } else {
3207         newarr[co] = arr[i];
3208       }
3209     }
3210     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3211     arr = newarr;
3212     n   = newn;
3213   }
3214   PetscCall(ISRestoreIndices(points, &arr0));
3215   *depth = depth_;
3216   if (expandedPoints) *expandedPoints = expandedPoints_;
3217   else {
3218     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3219     PetscCall(PetscFree(expandedPoints_));
3220   }
3221   if (sections) *sections = sections_;
3222   else {
3223     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3224     PetscCall(PetscFree(sections_));
3225   }
3226   PetscFunctionReturn(PETSC_SUCCESS);
3227 }
3228 
3229 /*@
3230   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3231 
3232   Not Collective
3233 
3234   Input Parameters:
3235 + dm     - The `DMPLEX`
3236 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3237 
3238   Output Parameters:
3239 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3240 . expandedPoints - (optional) An array of recursively expanded cones
3241 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3242 
3243   Level: advanced
3244 
3245   Note:
3246   See `DMPlexGetConeRecursive()`
3247 
3248 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3249           `DMPlexGetDepth()`, `IS`, `PetscSection`
3250 @*/
3251 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3252 {
3253   PetscInt d, depth_;
3254 
3255   PetscFunctionBegin;
3256   PetscCall(DMPlexGetDepth(dm, &depth_));
3257   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3258   if (depth) *depth = 0;
3259   if (expandedPoints) {
3260     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3261     PetscCall(PetscFree(*expandedPoints));
3262   }
3263   if (sections) {
3264     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3265     PetscCall(PetscFree(*sections));
3266   }
3267   PetscFunctionReturn(PETSC_SUCCESS);
3268 }
3269 
3270 /*@
3271   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3272 
3273   Not Collective
3274 
3275   Input Parameters:
3276 + dm   - The `DMPLEX`
3277 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3278 - cone - An array of points which are on the in-edges for point `p`
3279 
3280   Level: beginner
3281 
3282   Note:
3283   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3284 
3285 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3286 @*/
3287 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3288 {
3289   DM_Plex *mesh = (DM_Plex *)dm->data;
3290   PetscInt dof, off, c;
3291 
3292   PetscFunctionBegin;
3293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3294   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3295   if (dof) PetscAssertPointer(cone, 3);
3296   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3297   if (PetscDefined(USE_DEBUG)) {
3298     PetscInt pStart, pEnd;
3299     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3300     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3301     for (c = 0; c < dof; ++c) {
3302       PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3303       mesh->cones[off + c] = cone[c];
3304     }
3305   } else {
3306     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3307   }
3308   PetscFunctionReturn(PETSC_SUCCESS);
3309 }
3310 
3311 /*@C
3312   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3313 
3314   Not Collective
3315 
3316   Input Parameters:
3317 + dm - The `DMPLEX`
3318 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3319 
3320   Output Parameter:
3321 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3322                     integer giving the prescription for cone traversal.
3323 
3324   Level: beginner
3325 
3326   Note:
3327   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3328   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3329   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3330   with the identity.
3331 
3332   Fortran Notes:
3333   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3334   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3335 
3336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3337 @*/
3338 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3339 {
3340   DM_Plex *mesh = (DM_Plex *)dm->data;
3341   PetscInt off;
3342 
3343   PetscFunctionBegin;
3344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3345   if (PetscDefined(USE_DEBUG)) {
3346     PetscInt dof;
3347     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3348     if (dof) PetscAssertPointer(coneOrientation, 3);
3349   }
3350   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3351 
3352   *coneOrientation = &mesh->coneOrientations[off];
3353   PetscFunctionReturn(PETSC_SUCCESS);
3354 }
3355 
3356 /*@
3357   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3358 
3359   Not Collective
3360 
3361   Input Parameters:
3362 + dm              - The `DMPLEX`
3363 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3364 - coneOrientation - An array of orientations
3365 
3366   Level: beginner
3367 
3368   Notes:
3369   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3370 
3371   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3372 
3373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3374 @*/
3375 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3376 {
3377   DM_Plex *mesh = (DM_Plex *)dm->data;
3378   PetscInt pStart, pEnd;
3379   PetscInt dof, off, c;
3380 
3381   PetscFunctionBegin;
3382   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3383   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3384   if (dof) PetscAssertPointer(coneOrientation, 3);
3385   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3386   if (PetscDefined(USE_DEBUG)) {
3387     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3388     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3389     for (c = 0; c < dof; ++c) {
3390       PetscInt cdof, o = coneOrientation[c];
3391 
3392       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3393       PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3394       mesh->coneOrientations[off + c] = o;
3395     }
3396   } else {
3397     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3398   }
3399   PetscFunctionReturn(PETSC_SUCCESS);
3400 }
3401 
3402 /*@
3403   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3404 
3405   Not Collective
3406 
3407   Input Parameters:
3408 + dm        - The `DMPLEX`
3409 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3410 . conePos   - The local index in the cone where the point should be put
3411 - conePoint - The mesh point to insert
3412 
3413   Level: beginner
3414 
3415 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3416 @*/
3417 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3418 {
3419   DM_Plex *mesh = (DM_Plex *)dm->data;
3420   PetscInt pStart, pEnd;
3421   PetscInt dof, off;
3422 
3423   PetscFunctionBegin;
3424   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3425   if (PetscDefined(USE_DEBUG)) {
3426     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3427     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3428     PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3429     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3430     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3431   }
3432   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3433   mesh->cones[off + conePos] = conePoint;
3434   PetscFunctionReturn(PETSC_SUCCESS);
3435 }
3436 
3437 /*@
3438   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3439 
3440   Not Collective
3441 
3442   Input Parameters:
3443 + dm              - The `DMPLEX`
3444 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3445 . conePos         - The local index in the cone where the point should be put
3446 - coneOrientation - The point orientation to insert
3447 
3448   Level: beginner
3449 
3450   Note:
3451   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3452 
3453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3454 @*/
3455 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3456 {
3457   DM_Plex *mesh = (DM_Plex *)dm->data;
3458   PetscInt pStart, pEnd;
3459   PetscInt dof, off;
3460 
3461   PetscFunctionBegin;
3462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3463   if (PetscDefined(USE_DEBUG)) {
3464     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3465     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3466     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3467     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3468   }
3469   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3470   mesh->coneOrientations[off + conePos] = coneOrientation;
3471   PetscFunctionReturn(PETSC_SUCCESS);
3472 }
3473 
3474 /*@C
3475   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3476 
3477   Not collective
3478 
3479   Input Parameters:
3480 + dm - The DMPlex
3481 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3482 
3483   Output Parameters:
3484 + cone - An array of points which are on the in-edges for point `p`
3485 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3486         integer giving the prescription for cone traversal.
3487 
3488   Level: beginner
3489 
3490   Notes:
3491   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3492   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3493   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3494   with the identity.
3495 
3496   Fortran Notes:
3497   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3498   `DMPlexRestoreCone()` is not needed/available in C.
3499 
3500 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3501 @*/
3502 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3503 {
3504   DM_Plex *mesh = (DM_Plex *)dm->data;
3505 
3506   PetscFunctionBegin;
3507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3508   if (mesh->tr) {
3509     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3510   } else {
3511     PetscInt off;
3512     if (PetscDefined(USE_DEBUG)) {
3513       PetscInt dof;
3514       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3515       if (dof) {
3516         if (cone) PetscAssertPointer(cone, 3);
3517         if (ornt) PetscAssertPointer(ornt, 4);
3518       }
3519     }
3520     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3521     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3522     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3523   }
3524   PetscFunctionReturn(PETSC_SUCCESS);
3525 }
3526 
3527 /*@C
3528   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3529 
3530   Not Collective
3531 
3532   Input Parameters:
3533 + dm   - The DMPlex
3534 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3535 . cone - An array of points which are on the in-edges for point p
3536 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3537         integer giving the prescription for cone traversal.
3538 
3539   Level: beginner
3540 
3541   Notes:
3542   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3543   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3544   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3545   with the identity.
3546 
3547   Fortran Notes:
3548   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3549   `DMPlexRestoreCone()` is not needed/available in C.
3550 
3551 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3552 @*/
3553 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3554 {
3555   DM_Plex *mesh = (DM_Plex *)dm->data;
3556 
3557   PetscFunctionBegin;
3558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3559   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3560   PetscFunctionReturn(PETSC_SUCCESS);
3561 }
3562 
3563 /*@
3564   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3565 
3566   Not Collective
3567 
3568   Input Parameters:
3569 + dm - The `DMPLEX`
3570 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3571 
3572   Output Parameter:
3573 . size - The support size for point `p`
3574 
3575   Level: beginner
3576 
3577 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3578 @*/
3579 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3580 {
3581   DM_Plex *mesh = (DM_Plex *)dm->data;
3582 
3583   PetscFunctionBegin;
3584   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3585   PetscAssertPointer(size, 3);
3586   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3587   PetscFunctionReturn(PETSC_SUCCESS);
3588 }
3589 
3590 /*@
3591   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3592 
3593   Not Collective
3594 
3595   Input Parameters:
3596 + dm   - The `DMPLEX`
3597 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3598 - size - The support size for point `p`
3599 
3600   Level: beginner
3601 
3602   Note:
3603   This should be called after `DMPlexSetChart()`.
3604 
3605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3606 @*/
3607 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3608 {
3609   DM_Plex *mesh = (DM_Plex *)dm->data;
3610 
3611   PetscFunctionBegin;
3612   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3613   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3614   PetscFunctionReturn(PETSC_SUCCESS);
3615 }
3616 
3617 /*@C
3618   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3619 
3620   Not Collective
3621 
3622   Input Parameters:
3623 + dm - The `DMPLEX`
3624 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3625 
3626   Output Parameter:
3627 . support - An array of points which are on the out-edges for point `p`
3628 
3629   Level: beginner
3630 
3631   Fortran Notes:
3632   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3633   `DMPlexRestoreSupport()` is not needed/available in C.
3634 
3635 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3636 @*/
3637 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3638 {
3639   DM_Plex *mesh = (DM_Plex *)dm->data;
3640   PetscInt off;
3641 
3642   PetscFunctionBegin;
3643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3644   PetscAssertPointer(support, 3);
3645   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3646   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3647   PetscFunctionReturn(PETSC_SUCCESS);
3648 }
3649 
3650 /*@
3651   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3652 
3653   Not Collective
3654 
3655   Input Parameters:
3656 + dm      - The `DMPLEX`
3657 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3658 - support - An array of points which are on the out-edges for point `p`
3659 
3660   Level: beginner
3661 
3662   Note:
3663   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3664 
3665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3666 @*/
3667 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3668 {
3669   DM_Plex *mesh = (DM_Plex *)dm->data;
3670   PetscInt pStart, pEnd;
3671   PetscInt dof, off, c;
3672 
3673   PetscFunctionBegin;
3674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3675   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3676   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3677   if (dof) PetscAssertPointer(support, 3);
3678   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3679   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3680   for (c = 0; c < dof; ++c) {
3681     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3682     mesh->supports[off + c] = support[c];
3683   }
3684   PetscFunctionReturn(PETSC_SUCCESS);
3685 }
3686 
3687 /*@
3688   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3689 
3690   Not Collective
3691 
3692   Input Parameters:
3693 + dm           - The `DMPLEX`
3694 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3695 . supportPos   - The local index in the cone where the point should be put
3696 - supportPoint - The mesh point to insert
3697 
3698   Level: beginner
3699 
3700 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3701 @*/
3702 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3703 {
3704   DM_Plex *mesh = (DM_Plex *)dm->data;
3705   PetscInt pStart, pEnd;
3706   PetscInt dof, off;
3707 
3708   PetscFunctionBegin;
3709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3710   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3711   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3712   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3713   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3714   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3715   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3716   mesh->supports[off + supportPos] = supportPoint;
3717   PetscFunctionReturn(PETSC_SUCCESS);
3718 }
3719 
3720 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3721 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3722 {
3723   switch (ct) {
3724   case DM_POLYTOPE_SEGMENT:
3725     if (o == -1) return -2;
3726     break;
3727   case DM_POLYTOPE_TRIANGLE:
3728     if (o == -3) return -1;
3729     if (o == -2) return -3;
3730     if (o == -1) return -2;
3731     break;
3732   case DM_POLYTOPE_QUADRILATERAL:
3733     if (o == -4) return -2;
3734     if (o == -3) return -1;
3735     if (o == -2) return -4;
3736     if (o == -1) return -3;
3737     break;
3738   default:
3739     return o;
3740   }
3741   return o;
3742 }
3743 
3744 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3745 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3746 {
3747   switch (ct) {
3748   case DM_POLYTOPE_SEGMENT:
3749     if ((o == -2) || (o == 1)) return -1;
3750     if (o == -1) return 0;
3751     break;
3752   case DM_POLYTOPE_TRIANGLE:
3753     if (o == -3) return -2;
3754     if (o == -2) return -1;
3755     if (o == -1) return -3;
3756     break;
3757   case DM_POLYTOPE_QUADRILATERAL:
3758     if (o == -4) return -2;
3759     if (o == -3) return -1;
3760     if (o == -2) return -4;
3761     if (o == -1) return -3;
3762     break;
3763   default:
3764     return o;
3765   }
3766   return o;
3767 }
3768 
3769 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3770 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3771 {
3772   PetscInt pStart, pEnd, p;
3773 
3774   PetscFunctionBegin;
3775   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3776   for (p = pStart; p < pEnd; ++p) {
3777     const PetscInt *cone, *ornt;
3778     PetscInt        coneSize, c;
3779 
3780     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3781     PetscCall(DMPlexGetCone(dm, p, &cone));
3782     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3783     for (c = 0; c < coneSize; ++c) {
3784       DMPolytopeType ct;
3785       const PetscInt o = ornt[c];
3786 
3787       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3788       switch (ct) {
3789       case DM_POLYTOPE_SEGMENT:
3790         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3791         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3792         break;
3793       case DM_POLYTOPE_TRIANGLE:
3794         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3795         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3796         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3797         break;
3798       case DM_POLYTOPE_QUADRILATERAL:
3799         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3800         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3801         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3802         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3803         break;
3804       default:
3805         break;
3806       }
3807     }
3808   }
3809   PetscFunctionReturn(PETSC_SUCCESS);
3810 }
3811 
3812 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3813 {
3814   DM_Plex *mesh = (DM_Plex *)dm->data;
3815 
3816   PetscFunctionBeginHot;
3817   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3818     if (useCone) {
3819       PetscCall(DMPlexGetConeSize(dm, p, size));
3820       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3821     } else {
3822       PetscCall(DMPlexGetSupportSize(dm, p, size));
3823       PetscCall(DMPlexGetSupport(dm, p, arr));
3824     }
3825   } else {
3826     if (useCone) {
3827       const PetscSection s   = mesh->coneSection;
3828       const PetscInt     ps  = p - s->pStart;
3829       const PetscInt     off = s->atlasOff[ps];
3830 
3831       *size = s->atlasDof[ps];
3832       *arr  = mesh->cones + off;
3833       *ornt = mesh->coneOrientations + off;
3834     } else {
3835       const PetscSection s   = mesh->supportSection;
3836       const PetscInt     ps  = p - s->pStart;
3837       const PetscInt     off = s->atlasOff[ps];
3838 
3839       *size = s->atlasDof[ps];
3840       *arr  = mesh->supports + off;
3841     }
3842   }
3843   PetscFunctionReturn(PETSC_SUCCESS);
3844 }
3845 
3846 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3847 {
3848   DM_Plex *mesh = (DM_Plex *)dm->data;
3849 
3850   PetscFunctionBeginHot;
3851   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3852     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3853   }
3854   PetscFunctionReturn(PETSC_SUCCESS);
3855 }
3856 
3857 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3858 {
3859   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3860   PetscInt       *closure;
3861   const PetscInt *tmp = NULL, *tmpO = NULL;
3862   PetscInt        off = 0, tmpSize, t;
3863 
3864   PetscFunctionBeginHot;
3865   if (ornt) {
3866     PetscCall(DMPlexGetCellType(dm, p, &ct));
3867     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
3868   }
3869   if (*points) {
3870     closure = *points;
3871   } else {
3872     PetscInt maxConeSize, maxSupportSize;
3873     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3874     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3875   }
3876   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3877   if (ct == DM_POLYTOPE_UNKNOWN) {
3878     closure[off++] = p;
3879     closure[off++] = 0;
3880     for (t = 0; t < tmpSize; ++t) {
3881       closure[off++] = tmp[t];
3882       closure[off++] = tmpO ? tmpO[t] : 0;
3883     }
3884   } else {
3885     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3886 
3887     /* We assume that cells with a valid type have faces with a valid type */
3888     closure[off++] = p;
3889     closure[off++] = ornt;
3890     for (t = 0; t < tmpSize; ++t) {
3891       DMPolytopeType ft;
3892 
3893       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3894       closure[off++] = tmp[arr[t]];
3895       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3896     }
3897   }
3898   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3899   if (numPoints) *numPoints = tmpSize + 1;
3900   if (points) *points = closure;
3901   PetscFunctionReturn(PETSC_SUCCESS);
3902 }
3903 
3904 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3905 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3906 {
3907   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3908   const PetscInt *cone, *ornt;
3909   PetscInt       *pts, *closure = NULL;
3910   DMPolytopeType  ft;
3911   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3912   PetscInt        dim, coneSize, c, d, clSize, cl;
3913 
3914   PetscFunctionBeginHot;
3915   PetscCall(DMGetDimension(dm, &dim));
3916   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3917   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3918   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3919   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3920   maxSize       = PetscMax(coneSeries, supportSeries);
3921   if (*points) {
3922     pts = *points;
3923   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3924   c        = 0;
3925   pts[c++] = point;
3926   pts[c++] = o;
3927   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3928   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3929   for (cl = 0; cl < clSize * 2; cl += 2) {
3930     pts[c++] = closure[cl];
3931     pts[c++] = closure[cl + 1];
3932   }
3933   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3934   for (cl = 0; cl < clSize * 2; cl += 2) {
3935     pts[c++] = closure[cl];
3936     pts[c++] = closure[cl + 1];
3937   }
3938   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3939   for (d = 2; d < coneSize; ++d) {
3940     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3941     pts[c++] = cone[arr[d * 2 + 0]];
3942     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3943   }
3944   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3945   if (dim >= 3) {
3946     for (d = 2; d < coneSize; ++d) {
3947       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3948       const PetscInt *fcone, *fornt;
3949       PetscInt        fconeSize, fc, i;
3950 
3951       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3952       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3953       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3954       for (fc = 0; fc < fconeSize; ++fc) {
3955         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3956         const PetscInt co = farr[fc * 2 + 1];
3957 
3958         for (i = 0; i < c; i += 2)
3959           if (pts[i] == cp) break;
3960         if (i == c) {
3961           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3962           pts[c++] = cp;
3963           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3964         }
3965       }
3966       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3967     }
3968   }
3969   *numPoints = c / 2;
3970   *points    = pts;
3971   PetscFunctionReturn(PETSC_SUCCESS);
3972 }
3973 
3974 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3975 {
3976   DMPolytopeType ct;
3977   PetscInt      *closure, *fifo;
3978   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3979   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3980   PetscInt       depth, maxSize;
3981 
3982   PetscFunctionBeginHot;
3983   PetscCall(DMPlexGetDepth(dm, &depth));
3984   if (depth == 1) {
3985     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3986     PetscFunctionReturn(PETSC_SUCCESS);
3987   }
3988   PetscCall(DMPlexGetCellType(dm, p, &ct));
3989   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
3990   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3991     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3992     PetscFunctionReturn(PETSC_SUCCESS);
3993   }
3994   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3995   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3996   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3997   maxSize       = PetscMax(coneSeries, supportSeries);
3998   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3999   if (*points) {
4000     closure = *points;
4001   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4002   closure[closureSize++] = p;
4003   closure[closureSize++] = ornt;
4004   fifo[fifoSize++]       = p;
4005   fifo[fifoSize++]       = ornt;
4006   fifo[fifoSize++]       = ct;
4007   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4008   while (fifoSize - fifoStart) {
4009     const PetscInt       q    = fifo[fifoStart++];
4010     const PetscInt       o    = fifo[fifoStart++];
4011     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4012     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4013     const PetscInt      *tmp, *tmpO = NULL;
4014     PetscInt             tmpSize, t;
4015 
4016     if (PetscDefined(USE_DEBUG)) {
4017       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4018       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
4019     }
4020     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4021     for (t = 0; t < tmpSize; ++t) {
4022       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4023       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4024       const PetscInt cp = tmp[ip];
4025       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4026       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4027       PetscInt       c;
4028 
4029       /* Check for duplicate */
4030       for (c = 0; c < closureSize; c += 2) {
4031         if (closure[c] == cp) break;
4032       }
4033       if (c == closureSize) {
4034         closure[closureSize++] = cp;
4035         closure[closureSize++] = co;
4036         fifo[fifoSize++]       = cp;
4037         fifo[fifoSize++]       = co;
4038         fifo[fifoSize++]       = ct;
4039       }
4040     }
4041     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4042   }
4043   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4044   if (numPoints) *numPoints = closureSize / 2;
4045   if (points) *points = closure;
4046   PetscFunctionReturn(PETSC_SUCCESS);
4047 }
4048 
4049 /*@C
4050   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4051 
4052   Not Collective
4053 
4054   Input Parameters:
4055 + dm      - The `DMPLEX`
4056 . p       - The mesh point
4057 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4058 
4059   Input/Output Parameter:
4060 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4061            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4062 
4063   Output Parameter:
4064 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4065 
4066   Level: beginner
4067 
4068   Note:
4069   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4070 
4071   Fortran Notes:
4072   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4073 
4074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4075 @*/
4076 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4077 {
4078   PetscFunctionBeginHot;
4079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4080   if (numPoints) PetscAssertPointer(numPoints, 4);
4081   if (points) PetscAssertPointer(points, 5);
4082   if (PetscDefined(USE_DEBUG)) {
4083     PetscInt pStart, pEnd;
4084     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4085     PetscCheck(p >= pStart && p < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " is not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
4086   }
4087   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4088   PetscFunctionReturn(PETSC_SUCCESS);
4089 }
4090 
4091 /*@C
4092   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4093 
4094   Not Collective
4095 
4096   Input Parameters:
4097 + dm        - The `DMPLEX`
4098 . p         - The mesh point
4099 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4100 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4101 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4102 
4103   Level: beginner
4104 
4105   Note:
4106   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4107 
4108 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4109 @*/
4110 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4111 {
4112   PetscFunctionBeginHot;
4113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4114   if (numPoints) *numPoints = 0;
4115   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4116   PetscFunctionReturn(PETSC_SUCCESS);
4117 }
4118 
4119 /*@
4120   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4121 
4122   Not Collective
4123 
4124   Input Parameter:
4125 . dm - The `DMPLEX`
4126 
4127   Output Parameters:
4128 + maxConeSize    - The maximum number of in-edges
4129 - maxSupportSize - The maximum number of out-edges
4130 
4131   Level: beginner
4132 
4133 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4134 @*/
4135 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4136 {
4137   DM_Plex *mesh = (DM_Plex *)dm->data;
4138 
4139   PetscFunctionBegin;
4140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4141   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4142   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4143   PetscFunctionReturn(PETSC_SUCCESS);
4144 }
4145 
4146 PetscErrorCode DMSetUp_Plex(DM dm)
4147 {
4148   DM_Plex *mesh = (DM_Plex *)dm->data;
4149   PetscInt size, maxSupportSize;
4150 
4151   PetscFunctionBegin;
4152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4153   PetscCall(PetscSectionSetUp(mesh->coneSection));
4154   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4155   PetscCall(PetscMalloc1(size, &mesh->cones));
4156   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4157   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4158   if (maxSupportSize) {
4159     PetscCall(PetscSectionSetUp(mesh->supportSection));
4160     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4161     PetscCall(PetscMalloc1(size, &mesh->supports));
4162   }
4163   PetscFunctionReturn(PETSC_SUCCESS);
4164 }
4165 
4166 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4167 {
4168   PetscFunctionBegin;
4169   if (subdm) PetscCall(DMClone(dm, subdm));
4170   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4171   if (subdm) (*subdm)->useNatural = dm->useNatural;
4172   if (dm->useNatural && dm->sfMigration) {
4173     PetscSF sfNatural;
4174 
4175     (*subdm)->sfMigration = dm->sfMigration;
4176     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4177     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4178     (*subdm)->sfNatural = sfNatural;
4179   }
4180   PetscFunctionReturn(PETSC_SUCCESS);
4181 }
4182 
4183 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4184 {
4185   PetscInt i = 0;
4186 
4187   PetscFunctionBegin;
4188   PetscCall(DMClone(dms[0], superdm));
4189   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4190   (*superdm)->useNatural = PETSC_FALSE;
4191   for (i = 0; i < len; i++) {
4192     if (dms[i]->useNatural && dms[i]->sfMigration) {
4193       PetscSF sfNatural;
4194 
4195       (*superdm)->sfMigration = dms[i]->sfMigration;
4196       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4197       (*superdm)->useNatural = PETSC_TRUE;
4198       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4199       (*superdm)->sfNatural = sfNatural;
4200       break;
4201     }
4202   }
4203   PetscFunctionReturn(PETSC_SUCCESS);
4204 }
4205 
4206 /*@
4207   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4208 
4209   Not Collective
4210 
4211   Input Parameter:
4212 . dm - The `DMPLEX`
4213 
4214   Level: beginner
4215 
4216   Note:
4217   This should be called after all calls to `DMPlexSetCone()`
4218 
4219 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4220 @*/
4221 PetscErrorCode DMPlexSymmetrize(DM dm)
4222 {
4223   DM_Plex  *mesh = (DM_Plex *)dm->data;
4224   PetscInt *offsets;
4225   PetscInt  supportSize;
4226   PetscInt  pStart, pEnd, p;
4227 
4228   PetscFunctionBegin;
4229   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4230   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4231   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4232   /* Calculate support sizes */
4233   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4234   for (p = pStart; p < pEnd; ++p) {
4235     PetscInt dof, off, c;
4236 
4237     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4238     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4239     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4240   }
4241   PetscCall(PetscSectionSetUp(mesh->supportSection));
4242   /* Calculate supports */
4243   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4244   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4245   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4246   for (p = pStart; p < pEnd; ++p) {
4247     PetscInt dof, off, c;
4248 
4249     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4250     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4251     for (c = off; c < off + dof; ++c) {
4252       const PetscInt q = mesh->cones[c];
4253       PetscInt       offS;
4254 
4255       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4256 
4257       mesh->supports[offS + offsets[q]] = p;
4258       ++offsets[q];
4259     }
4260   }
4261   PetscCall(PetscFree(offsets));
4262   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4263   PetscFunctionReturn(PETSC_SUCCESS);
4264 }
4265 
4266 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4267 {
4268   IS stratumIS;
4269 
4270   PetscFunctionBegin;
4271   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4272   if (PetscDefined(USE_DEBUG)) {
4273     PetscInt  qStart, qEnd, numLevels, level;
4274     PetscBool overlap = PETSC_FALSE;
4275     PetscCall(DMLabelGetNumValues(label, &numLevels));
4276     for (level = 0; level < numLevels; level++) {
4277       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4278       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4279         overlap = PETSC_TRUE;
4280         break;
4281       }
4282     }
4283     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);
4284   }
4285   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4286   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4287   PetscCall(ISDestroy(&stratumIS));
4288   PetscFunctionReturn(PETSC_SUCCESS);
4289 }
4290 
4291 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4292 {
4293   PetscInt *pMin, *pMax;
4294   PetscInt  pStart, pEnd;
4295   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4296 
4297   PetscFunctionBegin;
4298   {
4299     DMLabel label2;
4300 
4301     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4302     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4303   }
4304   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4305   for (PetscInt p = pStart; p < pEnd; ++p) {
4306     DMPolytopeType ct;
4307 
4308     PetscCall(DMPlexGetCellType(dm, p, &ct));
4309     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4310     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4311   }
4312   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4313   for (PetscInt d = dmin; d <= dmax; ++d) {
4314     pMin[d] = PETSC_MAX_INT;
4315     pMax[d] = PETSC_MIN_INT;
4316   }
4317   for (PetscInt p = pStart; p < pEnd; ++p) {
4318     DMPolytopeType ct;
4319     PetscInt       d;
4320 
4321     PetscCall(DMPlexGetCellType(dm, p, &ct));
4322     d       = DMPolytopeTypeGetDim(ct);
4323     pMin[d] = PetscMin(p, pMin[d]);
4324     pMax[d] = PetscMax(p, pMax[d]);
4325   }
4326   for (PetscInt d = dmin; d <= dmax; ++d) {
4327     if (pMin[d] > pMax[d]) continue;
4328     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4329   }
4330   PetscCall(PetscFree2(pMin, pMax));
4331   PetscFunctionReturn(PETSC_SUCCESS);
4332 }
4333 
4334 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4335 {
4336   PetscInt pStart, pEnd;
4337   PetscInt numRoots = 0, numLeaves = 0;
4338 
4339   PetscFunctionBegin;
4340   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4341   {
4342     /* Initialize roots and count leaves */
4343     PetscInt sMin = PETSC_MAX_INT;
4344     PetscInt sMax = PETSC_MIN_INT;
4345     PetscInt coneSize, supportSize;
4346 
4347     for (PetscInt p = pStart; p < pEnd; ++p) {
4348       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4349       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4350       if (!coneSize && supportSize) {
4351         sMin = PetscMin(p, sMin);
4352         sMax = PetscMax(p, sMax);
4353         ++numRoots;
4354       } else if (!supportSize && coneSize) {
4355         ++numLeaves;
4356       } else if (!supportSize && !coneSize) {
4357         /* Isolated points */
4358         sMin = PetscMin(p, sMin);
4359         sMax = PetscMax(p, sMax);
4360       }
4361     }
4362     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4363   }
4364 
4365   if (numRoots + numLeaves == (pEnd - pStart)) {
4366     PetscInt sMin = PETSC_MAX_INT;
4367     PetscInt sMax = PETSC_MIN_INT;
4368     PetscInt coneSize, supportSize;
4369 
4370     for (PetscInt p = pStart; p < pEnd; ++p) {
4371       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4372       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4373       if (!supportSize && coneSize) {
4374         sMin = PetscMin(p, sMin);
4375         sMax = PetscMax(p, sMax);
4376       }
4377     }
4378     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4379   } else {
4380     PetscInt level = 0;
4381     PetscInt qStart, qEnd;
4382 
4383     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4384     while (qEnd > qStart) {
4385       PetscInt sMin = PETSC_MAX_INT;
4386       PetscInt sMax = PETSC_MIN_INT;
4387 
4388       for (PetscInt q = qStart; q < qEnd; ++q) {
4389         const PetscInt *support;
4390         PetscInt        supportSize;
4391 
4392         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4393         PetscCall(DMPlexGetSupport(dm, q, &support));
4394         for (PetscInt s = 0; s < supportSize; ++s) {
4395           sMin = PetscMin(support[s], sMin);
4396           sMax = PetscMax(support[s], sMax);
4397         }
4398       }
4399       PetscCall(DMLabelGetNumValues(label, &level));
4400       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4401       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4402     }
4403   }
4404   PetscFunctionReturn(PETSC_SUCCESS);
4405 }
4406 
4407 /*@
4408   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4409 
4410   Collective
4411 
4412   Input Parameter:
4413 . dm - The `DMPLEX`
4414 
4415   Level: beginner
4416 
4417   Notes:
4418   The strata group all points of the same grade, and this function calculates the strata. This
4419   grade can be seen as the height (or depth) of the point in the DAG.
4420 
4421   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4422   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4423   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4424   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4425   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4426   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4427   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4428 
4429   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4430   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4431   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
4432   to interpolate only that one (e0), so that
4433 .vb
4434   cone(c0) = {e0, v2}
4435   cone(e0) = {v0, v1}
4436 .ve
4437   If `DMPlexStratify()` is run on this mesh, it will give depths
4438 .vb
4439    depth 0 = {v0, v1, v2}
4440    depth 1 = {e0, c0}
4441 .ve
4442   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4443 
4444   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4445 
4446 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4447 @*/
4448 PetscErrorCode DMPlexStratify(DM dm)
4449 {
4450   DM_Plex  *mesh = (DM_Plex *)dm->data;
4451   DMLabel   label;
4452   PetscBool flg = PETSC_FALSE;
4453 
4454   PetscFunctionBegin;
4455   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4456   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4457 
4458   // Create depth label
4459   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4460   PetscCall(DMCreateLabel(dm, "depth"));
4461   PetscCall(DMPlexGetDepthLabel(dm, &label));
4462 
4463   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4464   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4465   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4466 
4467   { /* just in case there is an empty process */
4468     PetscInt numValues, maxValues = 0, v;
4469 
4470     PetscCall(DMLabelGetNumValues(label, &numValues));
4471     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4472     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4473   }
4474   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4475   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4476   PetscFunctionReturn(PETSC_SUCCESS);
4477 }
4478 
4479 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4480 {
4481   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4482   PetscInt       dim, depth, pheight, coneSize;
4483 
4484   PetscFunctionBeginHot;
4485   PetscCall(DMGetDimension(dm, &dim));
4486   PetscCall(DMPlexGetDepth(dm, &depth));
4487   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4488   pheight = depth - pdepth;
4489   if (depth <= 1) {
4490     switch (pdepth) {
4491     case 0:
4492       ct = DM_POLYTOPE_POINT;
4493       break;
4494     case 1:
4495       switch (coneSize) {
4496       case 2:
4497         ct = DM_POLYTOPE_SEGMENT;
4498         break;
4499       case 3:
4500         ct = DM_POLYTOPE_TRIANGLE;
4501         break;
4502       case 4:
4503         switch (dim) {
4504         case 2:
4505           ct = DM_POLYTOPE_QUADRILATERAL;
4506           break;
4507         case 3:
4508           ct = DM_POLYTOPE_TETRAHEDRON;
4509           break;
4510         default:
4511           break;
4512         }
4513         break;
4514       case 5:
4515         ct = DM_POLYTOPE_PYRAMID;
4516         break;
4517       case 6:
4518         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4519         break;
4520       case 8:
4521         ct = DM_POLYTOPE_HEXAHEDRON;
4522         break;
4523       default:
4524         break;
4525       }
4526     }
4527   } else {
4528     if (pdepth == 0) {
4529       ct = DM_POLYTOPE_POINT;
4530     } else if (pheight == 0) {
4531       switch (dim) {
4532       case 1:
4533         switch (coneSize) {
4534         case 2:
4535           ct = DM_POLYTOPE_SEGMENT;
4536           break;
4537         default:
4538           break;
4539         }
4540         break;
4541       case 2:
4542         switch (coneSize) {
4543         case 3:
4544           ct = DM_POLYTOPE_TRIANGLE;
4545           break;
4546         case 4:
4547           ct = DM_POLYTOPE_QUADRILATERAL;
4548           break;
4549         default:
4550           break;
4551         }
4552         break;
4553       case 3:
4554         switch (coneSize) {
4555         case 4:
4556           ct = DM_POLYTOPE_TETRAHEDRON;
4557           break;
4558         case 5: {
4559           const PetscInt *cone;
4560           PetscInt        faceConeSize;
4561 
4562           PetscCall(DMPlexGetCone(dm, p, &cone));
4563           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4564           switch (faceConeSize) {
4565           case 3:
4566             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4567             break;
4568           case 4:
4569             ct = DM_POLYTOPE_PYRAMID;
4570             break;
4571           }
4572         } break;
4573         case 6:
4574           ct = DM_POLYTOPE_HEXAHEDRON;
4575           break;
4576         default:
4577           break;
4578         }
4579         break;
4580       default:
4581         break;
4582       }
4583     } else if (pheight > 0) {
4584       switch (coneSize) {
4585       case 2:
4586         ct = DM_POLYTOPE_SEGMENT;
4587         break;
4588       case 3:
4589         ct = DM_POLYTOPE_TRIANGLE;
4590         break;
4591       case 4:
4592         ct = DM_POLYTOPE_QUADRILATERAL;
4593         break;
4594       default:
4595         break;
4596       }
4597     }
4598   }
4599   *pt = ct;
4600   PetscFunctionReturn(PETSC_SUCCESS);
4601 }
4602 
4603 /*@
4604   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4605 
4606   Collective
4607 
4608   Input Parameter:
4609 . dm - The `DMPLEX`
4610 
4611   Level: developer
4612 
4613   Note:
4614   This function is normally called automatically when a cell type is requested. It creates an
4615   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4616   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4617 
4618   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4619 
4620 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4621 @*/
4622 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4623 {
4624   DM_Plex *mesh;
4625   DMLabel  ctLabel;
4626   PetscInt pStart, pEnd, p;
4627 
4628   PetscFunctionBegin;
4629   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4630   mesh = (DM_Plex *)dm->data;
4631   PetscCall(DMCreateLabel(dm, "celltype"));
4632   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4633   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4634   PetscCall(PetscFree(mesh->cellTypes));
4635   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4636   for (p = pStart; p < pEnd; ++p) {
4637     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4638     PetscInt       pdepth;
4639 
4640     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4641     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4642     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4643     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4644     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4645   }
4646   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4647   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4648   PetscFunctionReturn(PETSC_SUCCESS);
4649 }
4650 
4651 /*@C
4652   DMPlexGetJoin - Get an array for the join of the set of points
4653 
4654   Not Collective
4655 
4656   Input Parameters:
4657 + dm        - The `DMPLEX` object
4658 . numPoints - The number of input points for the join
4659 - points    - The input points
4660 
4661   Output Parameters:
4662 + numCoveredPoints - The number of points in the join
4663 - coveredPoints    - The points in the join
4664 
4665   Level: intermediate
4666 
4667   Note:
4668   Currently, this is restricted to a single level join
4669 
4670   Fortran Notes:
4671   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4672 
4673 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4674 @*/
4675 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4676 {
4677   DM_Plex  *mesh = (DM_Plex *)dm->data;
4678   PetscInt *join[2];
4679   PetscInt  joinSize, i = 0;
4680   PetscInt  dof, off, p, c, m;
4681   PetscInt  maxSupportSize;
4682 
4683   PetscFunctionBegin;
4684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4685   PetscAssertPointer(points, 3);
4686   PetscAssertPointer(numCoveredPoints, 4);
4687   PetscAssertPointer(coveredPoints, 5);
4688   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4689   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4690   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4691   /* Copy in support of first point */
4692   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4693   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4694   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4695   /* Check each successive support */
4696   for (p = 1; p < numPoints; ++p) {
4697     PetscInt newJoinSize = 0;
4698 
4699     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4700     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4701     for (c = 0; c < dof; ++c) {
4702       const PetscInt point = mesh->supports[off + c];
4703 
4704       for (m = 0; m < joinSize; ++m) {
4705         if (point == join[i][m]) {
4706           join[1 - i][newJoinSize++] = point;
4707           break;
4708         }
4709       }
4710     }
4711     joinSize = newJoinSize;
4712     i        = 1 - i;
4713   }
4714   *numCoveredPoints = joinSize;
4715   *coveredPoints    = join[i];
4716   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4717   PetscFunctionReturn(PETSC_SUCCESS);
4718 }
4719 
4720 /*@C
4721   DMPlexRestoreJoin - Restore an array for the join of the set of points
4722 
4723   Not Collective
4724 
4725   Input Parameters:
4726 + dm        - The `DMPLEX` object
4727 . numPoints - The number of input points for the join
4728 - points    - The input points
4729 
4730   Output Parameters:
4731 + numCoveredPoints - The number of points in the join
4732 - coveredPoints    - The points in the join
4733 
4734   Level: intermediate
4735 
4736   Fortran Notes:
4737   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4738 
4739 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4740 @*/
4741 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4742 {
4743   PetscFunctionBegin;
4744   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4745   if (points) PetscAssertPointer(points, 3);
4746   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4747   PetscAssertPointer(coveredPoints, 5);
4748   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4749   if (numCoveredPoints) *numCoveredPoints = 0;
4750   PetscFunctionReturn(PETSC_SUCCESS);
4751 }
4752 
4753 /*@C
4754   DMPlexGetFullJoin - Get an array for the join of the set of points
4755 
4756   Not Collective
4757 
4758   Input Parameters:
4759 + dm        - The `DMPLEX` object
4760 . numPoints - The number of input points for the join
4761 - points    - The input points
4762 
4763   Output Parameters:
4764 + numCoveredPoints - The number of points in the join
4765 - coveredPoints    - The points in the join
4766 
4767   Level: intermediate
4768 
4769   Fortran Notes:
4770   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4771 
4772 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4773 @*/
4774 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4775 {
4776   PetscInt *offsets, **closures;
4777   PetscInt *join[2];
4778   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4779   PetscInt  p, d, c, m, ms;
4780 
4781   PetscFunctionBegin;
4782   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4783   PetscAssertPointer(points, 3);
4784   PetscAssertPointer(numCoveredPoints, 4);
4785   PetscAssertPointer(coveredPoints, 5);
4786 
4787   PetscCall(DMPlexGetDepth(dm, &depth));
4788   PetscCall(PetscCalloc1(numPoints, &closures));
4789   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4790   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4791   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4792   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4793   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4794 
4795   for (p = 0; p < numPoints; ++p) {
4796     PetscInt closureSize;
4797 
4798     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4799 
4800     offsets[p * (depth + 2) + 0] = 0;
4801     for (d = 0; d < depth + 1; ++d) {
4802       PetscInt pStart, pEnd, i;
4803 
4804       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4805       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4806         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4807           offsets[p * (depth + 2) + d + 1] = i;
4808           break;
4809         }
4810       }
4811       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4812     }
4813     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);
4814   }
4815   for (d = 0; d < depth + 1; ++d) {
4816     PetscInt dof;
4817 
4818     /* Copy in support of first point */
4819     dof = offsets[d + 1] - offsets[d];
4820     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4821     /* Check each successive cone */
4822     for (p = 1; p < numPoints && joinSize; ++p) {
4823       PetscInt newJoinSize = 0;
4824 
4825       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4826       for (c = 0; c < dof; ++c) {
4827         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4828 
4829         for (m = 0; m < joinSize; ++m) {
4830           if (point == join[i][m]) {
4831             join[1 - i][newJoinSize++] = point;
4832             break;
4833           }
4834         }
4835       }
4836       joinSize = newJoinSize;
4837       i        = 1 - i;
4838     }
4839     if (joinSize) break;
4840   }
4841   *numCoveredPoints = joinSize;
4842   *coveredPoints    = join[i];
4843   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4844   PetscCall(PetscFree(closures));
4845   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4846   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4847   PetscFunctionReturn(PETSC_SUCCESS);
4848 }
4849 
4850 /*@C
4851   DMPlexGetMeet - Get an array for the meet of the set of points
4852 
4853   Not Collective
4854 
4855   Input Parameters:
4856 + dm        - The `DMPLEX` object
4857 . numPoints - The number of input points for the meet
4858 - points    - The input points
4859 
4860   Output Parameters:
4861 + numCoveringPoints - The number of points in the meet
4862 - coveringPoints    - The points in the meet
4863 
4864   Level: intermediate
4865 
4866   Note:
4867   Currently, this is restricted to a single level meet
4868 
4869   Fortran Notes:
4870   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4871 
4872 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4873 @*/
4874 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4875 {
4876   DM_Plex  *mesh = (DM_Plex *)dm->data;
4877   PetscInt *meet[2];
4878   PetscInt  meetSize, i = 0;
4879   PetscInt  dof, off, p, c, m;
4880   PetscInt  maxConeSize;
4881 
4882   PetscFunctionBegin;
4883   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4884   PetscAssertPointer(points, 3);
4885   PetscAssertPointer(numCoveringPoints, 4);
4886   PetscAssertPointer(coveringPoints, 5);
4887   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4888   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4889   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4890   /* Copy in cone of first point */
4891   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4892   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4893   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4894   /* Check each successive cone */
4895   for (p = 1; p < numPoints; ++p) {
4896     PetscInt newMeetSize = 0;
4897 
4898     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4899     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4900     for (c = 0; c < dof; ++c) {
4901       const PetscInt point = mesh->cones[off + c];
4902 
4903       for (m = 0; m < meetSize; ++m) {
4904         if (point == meet[i][m]) {
4905           meet[1 - i][newMeetSize++] = point;
4906           break;
4907         }
4908       }
4909     }
4910     meetSize = newMeetSize;
4911     i        = 1 - i;
4912   }
4913   *numCoveringPoints = meetSize;
4914   *coveringPoints    = meet[i];
4915   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4916   PetscFunctionReturn(PETSC_SUCCESS);
4917 }
4918 
4919 /*@C
4920   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4921 
4922   Not Collective
4923 
4924   Input Parameters:
4925 + dm        - The `DMPLEX` object
4926 . numPoints - The number of input points for the meet
4927 - points    - The input points
4928 
4929   Output Parameters:
4930 + numCoveredPoints - The number of points in the meet
4931 - coveredPoints    - The points in the meet
4932 
4933   Level: intermediate
4934 
4935   Fortran Notes:
4936   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4937 
4938 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4939 @*/
4940 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4941 {
4942   PetscFunctionBegin;
4943   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4944   if (points) PetscAssertPointer(points, 3);
4945   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4946   PetscAssertPointer(coveredPoints, 5);
4947   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4948   if (numCoveredPoints) *numCoveredPoints = 0;
4949   PetscFunctionReturn(PETSC_SUCCESS);
4950 }
4951 
4952 /*@C
4953   DMPlexGetFullMeet - Get an array for the meet of the set of points
4954 
4955   Not Collective
4956 
4957   Input Parameters:
4958 + dm        - The `DMPLEX` object
4959 . numPoints - The number of input points for the meet
4960 - points    - The input points
4961 
4962   Output Parameters:
4963 + numCoveredPoints - The number of points in the meet
4964 - coveredPoints    - The points in the meet
4965 
4966   Level: intermediate
4967 
4968   Fortran Notes:
4969   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4970 
4971 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4972 @*/
4973 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4974 {
4975   PetscInt *offsets, **closures;
4976   PetscInt *meet[2];
4977   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4978   PetscInt  p, h, c, m, mc;
4979 
4980   PetscFunctionBegin;
4981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4982   PetscAssertPointer(points, 3);
4983   PetscAssertPointer(numCoveredPoints, 4);
4984   PetscAssertPointer(coveredPoints, 5);
4985 
4986   PetscCall(DMPlexGetDepth(dm, &height));
4987   PetscCall(PetscMalloc1(numPoints, &closures));
4988   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4989   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4990   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4991   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4992   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4993 
4994   for (p = 0; p < numPoints; ++p) {
4995     PetscInt closureSize;
4996 
4997     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4998 
4999     offsets[p * (height + 2) + 0] = 0;
5000     for (h = 0; h < height + 1; ++h) {
5001       PetscInt pStart, pEnd, i;
5002 
5003       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5004       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5005         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5006           offsets[p * (height + 2) + h + 1] = i;
5007           break;
5008         }
5009       }
5010       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5011     }
5012     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);
5013   }
5014   for (h = 0; h < height + 1; ++h) {
5015     PetscInt dof;
5016 
5017     /* Copy in cone of first point */
5018     dof = offsets[h + 1] - offsets[h];
5019     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5020     /* Check each successive cone */
5021     for (p = 1; p < numPoints && meetSize; ++p) {
5022       PetscInt newMeetSize = 0;
5023 
5024       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5025       for (c = 0; c < dof; ++c) {
5026         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5027 
5028         for (m = 0; m < meetSize; ++m) {
5029           if (point == meet[i][m]) {
5030             meet[1 - i][newMeetSize++] = point;
5031             break;
5032           }
5033         }
5034       }
5035       meetSize = newMeetSize;
5036       i        = 1 - i;
5037     }
5038     if (meetSize) break;
5039   }
5040   *numCoveredPoints = meetSize;
5041   *coveredPoints    = meet[i];
5042   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5043   PetscCall(PetscFree(closures));
5044   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5045   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5046   PetscFunctionReturn(PETSC_SUCCESS);
5047 }
5048 
5049 /*@
5050   DMPlexEqual - Determine if two `DM` have the same topology
5051 
5052   Not Collective
5053 
5054   Input Parameters:
5055 + dmA - A `DMPLEX` object
5056 - dmB - A `DMPLEX` object
5057 
5058   Output Parameter:
5059 . equal - `PETSC_TRUE` if the topologies are identical
5060 
5061   Level: intermediate
5062 
5063   Note:
5064   We are not solving graph isomorphism, so we do not permute.
5065 
5066 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5067 @*/
5068 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5069 {
5070   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5071 
5072   PetscFunctionBegin;
5073   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5074   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5075   PetscAssertPointer(equal, 3);
5076 
5077   *equal = PETSC_FALSE;
5078   PetscCall(DMPlexGetDepth(dmA, &depth));
5079   PetscCall(DMPlexGetDepth(dmB, &depthB));
5080   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5081   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5082   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5083   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5084   for (p = pStart; p < pEnd; ++p) {
5085     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5086     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5087 
5088     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5089     PetscCall(DMPlexGetCone(dmA, p, &cone));
5090     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5091     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5092     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5093     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5094     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5095     for (c = 0; c < coneSize; ++c) {
5096       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5097       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5098     }
5099     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5100     PetscCall(DMPlexGetSupport(dmA, p, &support));
5101     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5102     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5103     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5104     for (s = 0; s < supportSize; ++s) {
5105       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5106     }
5107   }
5108   *equal = PETSC_TRUE;
5109   PetscFunctionReturn(PETSC_SUCCESS);
5110 }
5111 
5112 /*@
5113   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5114 
5115   Not Collective
5116 
5117   Input Parameters:
5118 + dm         - The `DMPLEX`
5119 . cellDim    - The cell dimension
5120 - numCorners - The number of vertices on a cell
5121 
5122   Output Parameter:
5123 . numFaceVertices - The number of vertices on a face
5124 
5125   Level: developer
5126 
5127   Note:
5128   Of course this can only work for a restricted set of symmetric shapes
5129 
5130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5131 @*/
5132 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5133 {
5134   MPI_Comm comm;
5135 
5136   PetscFunctionBegin;
5137   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5138   PetscAssertPointer(numFaceVertices, 4);
5139   switch (cellDim) {
5140   case 0:
5141     *numFaceVertices = 0;
5142     break;
5143   case 1:
5144     *numFaceVertices = 1;
5145     break;
5146   case 2:
5147     switch (numCorners) {
5148     case 3:                 /* triangle */
5149       *numFaceVertices = 2; /* Edge has 2 vertices */
5150       break;
5151     case 4:                 /* quadrilateral */
5152       *numFaceVertices = 2; /* Edge has 2 vertices */
5153       break;
5154     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5155       *numFaceVertices = 3; /* Edge has 3 vertices */
5156       break;
5157     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5158       *numFaceVertices = 3; /* Edge has 3 vertices */
5159       break;
5160     default:
5161       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5162     }
5163     break;
5164   case 3:
5165     switch (numCorners) {
5166     case 4:                 /* tetradehdron */
5167       *numFaceVertices = 3; /* Face has 3 vertices */
5168       break;
5169     case 6:                 /* tet cohesive cells */
5170       *numFaceVertices = 4; /* Face has 4 vertices */
5171       break;
5172     case 8:                 /* hexahedron */
5173       *numFaceVertices = 4; /* Face has 4 vertices */
5174       break;
5175     case 9:                 /* tet cohesive Lagrange cells */
5176       *numFaceVertices = 6; /* Face has 6 vertices */
5177       break;
5178     case 10:                /* quadratic tetrahedron */
5179       *numFaceVertices = 6; /* Face has 6 vertices */
5180       break;
5181     case 12:                /* hex cohesive Lagrange cells */
5182       *numFaceVertices = 6; /* Face has 6 vertices */
5183       break;
5184     case 18:                /* quadratic tet cohesive Lagrange cells */
5185       *numFaceVertices = 6; /* Face has 6 vertices */
5186       break;
5187     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5188       *numFaceVertices = 9; /* Face has 9 vertices */
5189       break;
5190     default:
5191       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5192     }
5193     break;
5194   default:
5195     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5196   }
5197   PetscFunctionReturn(PETSC_SUCCESS);
5198 }
5199 
5200 /*@
5201   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5202 
5203   Not Collective
5204 
5205   Input Parameter:
5206 . dm - The `DMPLEX` object
5207 
5208   Output Parameter:
5209 . depthLabel - The `DMLabel` recording point depth
5210 
5211   Level: developer
5212 
5213 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5214 @*/
5215 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5216 {
5217   PetscFunctionBegin;
5218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5219   PetscAssertPointer(depthLabel, 2);
5220   *depthLabel = dm->depthLabel;
5221   PetscFunctionReturn(PETSC_SUCCESS);
5222 }
5223 
5224 /*@
5225   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5226 
5227   Not Collective
5228 
5229   Input Parameter:
5230 . dm - The `DMPLEX` object
5231 
5232   Output Parameter:
5233 . depth - The number of strata (breadth first levels) in the DAG
5234 
5235   Level: developer
5236 
5237   Notes:
5238   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5239 
5240   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5241 
5242   An empty mesh gives -1.
5243 
5244 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5245 @*/
5246 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5247 {
5248   DM_Plex *mesh = (DM_Plex *)dm->data;
5249   DMLabel  label;
5250   PetscInt d = -1;
5251 
5252   PetscFunctionBegin;
5253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5254   PetscAssertPointer(depth, 2);
5255   if (mesh->tr) {
5256     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5257   } else {
5258     PetscCall(DMPlexGetDepthLabel(dm, &label));
5259     // Allow missing depths
5260     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5261     *depth = d;
5262   }
5263   PetscFunctionReturn(PETSC_SUCCESS);
5264 }
5265 
5266 /*@
5267   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5268 
5269   Not Collective
5270 
5271   Input Parameters:
5272 + dm    - The `DMPLEX` object
5273 - depth - The requested depth
5274 
5275   Output Parameters:
5276 + start - The first point at this `depth`
5277 - end   - One beyond the last point at this `depth`
5278 
5279   Level: developer
5280 
5281   Notes:
5282   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5283   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5284   higher dimension, e.g., "edges".
5285 
5286 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5287 @*/
5288 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5289 {
5290   DM_Plex *mesh = (DM_Plex *)dm->data;
5291   DMLabel  label;
5292   PetscInt pStart, pEnd;
5293 
5294   PetscFunctionBegin;
5295   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5296   if (start) {
5297     PetscAssertPointer(start, 3);
5298     *start = 0;
5299   }
5300   if (end) {
5301     PetscAssertPointer(end, 4);
5302     *end = 0;
5303   }
5304   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5305   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5306   if (depth < 0) {
5307     if (start) *start = pStart;
5308     if (end) *end = pEnd;
5309     PetscFunctionReturn(PETSC_SUCCESS);
5310   }
5311   if (mesh->tr) {
5312     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5313   } else {
5314     PetscCall(DMPlexGetDepthLabel(dm, &label));
5315     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5316     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5317   }
5318   PetscFunctionReturn(PETSC_SUCCESS);
5319 }
5320 
5321 /*@
5322   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5323 
5324   Not Collective
5325 
5326   Input Parameters:
5327 + dm     - The `DMPLEX` object
5328 - height - The requested height
5329 
5330   Output Parameters:
5331 + start - The first point at this `height`
5332 - end   - One beyond the last point at this `height`
5333 
5334   Level: developer
5335 
5336   Notes:
5337   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5338   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5339   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5340 
5341 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5342 @*/
5343 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5344 {
5345   DMLabel  label;
5346   PetscInt depth, pStart, pEnd;
5347 
5348   PetscFunctionBegin;
5349   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5350   if (start) {
5351     PetscAssertPointer(start, 3);
5352     *start = 0;
5353   }
5354   if (end) {
5355     PetscAssertPointer(end, 4);
5356     *end = 0;
5357   }
5358   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5359   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5360   if (height < 0) {
5361     if (start) *start = pStart;
5362     if (end) *end = pEnd;
5363     PetscFunctionReturn(PETSC_SUCCESS);
5364   }
5365   PetscCall(DMPlexGetDepthLabel(dm, &label));
5366   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5367   else PetscCall(DMGetDimension(dm, &depth));
5368   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5369   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5370   PetscFunctionReturn(PETSC_SUCCESS);
5371 }
5372 
5373 /*@
5374   DMPlexGetPointDepth - Get the `depth` of a given point
5375 
5376   Not Collective
5377 
5378   Input Parameters:
5379 + dm    - The `DMPLEX` object
5380 - point - The point
5381 
5382   Output Parameter:
5383 . depth - The depth of the `point`
5384 
5385   Level: intermediate
5386 
5387 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5388 @*/
5389 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5390 {
5391   PetscFunctionBegin;
5392   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5393   PetscAssertPointer(depth, 3);
5394   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5395   PetscFunctionReturn(PETSC_SUCCESS);
5396 }
5397 
5398 /*@
5399   DMPlexGetPointHeight - Get the `height` of a given point
5400 
5401   Not Collective
5402 
5403   Input Parameters:
5404 + dm    - The `DMPLEX` object
5405 - point - The point
5406 
5407   Output Parameter:
5408 . height - The height of the `point`
5409 
5410   Level: intermediate
5411 
5412 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5413 @*/
5414 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5415 {
5416   PetscInt n, pDepth;
5417 
5418   PetscFunctionBegin;
5419   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5420   PetscAssertPointer(height, 3);
5421   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5422   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5423   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5424   PetscFunctionReturn(PETSC_SUCCESS);
5425 }
5426 
5427 /*@
5428   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5429 
5430   Not Collective
5431 
5432   Input Parameter:
5433 . dm - The `DMPLEX` object
5434 
5435   Output Parameter:
5436 . celltypeLabel - The `DMLabel` recording cell polytope type
5437 
5438   Level: developer
5439 
5440   Note:
5441   This function will trigger automatica computation of cell types. This can be disabled by calling
5442   `DMCreateLabel`(dm, "celltype") beforehand.
5443 
5444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5445 @*/
5446 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5447 {
5448   PetscFunctionBegin;
5449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5450   PetscAssertPointer(celltypeLabel, 2);
5451   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5452   *celltypeLabel = dm->celltypeLabel;
5453   PetscFunctionReturn(PETSC_SUCCESS);
5454 }
5455 
5456 /*@
5457   DMPlexGetCellType - Get the polytope type of a given cell
5458 
5459   Not Collective
5460 
5461   Input Parameters:
5462 + dm   - The `DMPLEX` object
5463 - cell - The cell
5464 
5465   Output Parameter:
5466 . celltype - The polytope type of the cell
5467 
5468   Level: intermediate
5469 
5470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5471 @*/
5472 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5473 {
5474   DM_Plex *mesh = (DM_Plex *)dm->data;
5475   DMLabel  label;
5476   PetscInt ct;
5477 
5478   PetscFunctionBegin;
5479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5480   PetscAssertPointer(celltype, 3);
5481   if (mesh->tr) {
5482     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5483   } else {
5484     PetscInt pStart, pEnd;
5485 
5486     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5487     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5488       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5489       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5490       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5491       for (PetscInt p = pStart; p < pEnd; p++) {
5492         PetscCall(DMLabelGetValue(label, p, &ct));
5493         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5494       }
5495     }
5496     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5497     if (PetscDefined(USE_DEBUG)) {
5498       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5499       PetscCall(DMLabelGetValue(label, cell, &ct));
5500       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5501       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5502     }
5503   }
5504   PetscFunctionReturn(PETSC_SUCCESS);
5505 }
5506 
5507 /*@
5508   DMPlexSetCellType - Set the polytope type of a given cell
5509 
5510   Not Collective
5511 
5512   Input Parameters:
5513 + dm       - The `DMPLEX` object
5514 . cell     - The cell
5515 - celltype - The polytope type of the cell
5516 
5517   Level: advanced
5518 
5519   Note:
5520   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5521   is executed. This function will override the computed type. However, if automatic classification will not succeed
5522   and a user wants to manually specify all types, the classification must be disabled by calling
5523   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5524 
5525 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5526 @*/
5527 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5528 {
5529   DM_Plex *mesh = (DM_Plex *)dm->data;
5530   DMLabel  label;
5531   PetscInt pStart, pEnd;
5532 
5533   PetscFunctionBegin;
5534   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5535   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5536   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5537   PetscCall(DMLabelSetValue(label, cell, celltype));
5538   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5539   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5540   PetscFunctionReturn(PETSC_SUCCESS);
5541 }
5542 
5543 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5544 {
5545   PetscSection section;
5546   PetscInt     maxHeight;
5547   const char  *prefix;
5548 
5549   PetscFunctionBegin;
5550   PetscCall(DMClone(dm, cdm));
5551   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5552   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5553   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5554   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5555   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5556   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5557   PetscCall(DMSetLocalSection(*cdm, section));
5558   PetscCall(PetscSectionDestroy(&section));
5559 
5560   PetscCall(DMSetNumFields(*cdm, 1));
5561   PetscCall(DMCreateDS(*cdm));
5562   (*cdm)->cloneOpts = PETSC_TRUE;
5563   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5564   PetscFunctionReturn(PETSC_SUCCESS);
5565 }
5566 
5567 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5568 {
5569   Vec coordsLocal, cellCoordsLocal;
5570   DM  coordsDM, cellCoordsDM;
5571 
5572   PetscFunctionBegin;
5573   *field = NULL;
5574   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5575   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5576   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5577   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5578   if (coordsLocal && coordsDM) {
5579     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5580     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5581   }
5582   PetscFunctionReturn(PETSC_SUCCESS);
5583 }
5584 
5585 /*@
5586   DMPlexGetConeSection - Return a section which describes the layout of cone data
5587 
5588   Not Collective
5589 
5590   Input Parameter:
5591 . dm - The `DMPLEX` object
5592 
5593   Output Parameter:
5594 . section - The `PetscSection` object
5595 
5596   Level: developer
5597 
5598 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5599 @*/
5600 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5601 {
5602   DM_Plex *mesh = (DM_Plex *)dm->data;
5603 
5604   PetscFunctionBegin;
5605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5606   if (section) *section = mesh->coneSection;
5607   PetscFunctionReturn(PETSC_SUCCESS);
5608 }
5609 
5610 /*@
5611   DMPlexGetSupportSection - Return a section which describes the layout of support data
5612 
5613   Not Collective
5614 
5615   Input Parameter:
5616 . dm - The `DMPLEX` object
5617 
5618   Output Parameter:
5619 . section - The `PetscSection` object
5620 
5621   Level: developer
5622 
5623 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5624 @*/
5625 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5626 {
5627   DM_Plex *mesh = (DM_Plex *)dm->data;
5628 
5629   PetscFunctionBegin;
5630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5631   if (section) *section = mesh->supportSection;
5632   PetscFunctionReturn(PETSC_SUCCESS);
5633 }
5634 
5635 /*@C
5636   DMPlexGetCones - Return cone data
5637 
5638   Not Collective
5639 
5640   Input Parameter:
5641 . dm - The `DMPLEX` object
5642 
5643   Output Parameter:
5644 . cones - The cone for each point
5645 
5646   Level: developer
5647 
5648 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5649 @*/
5650 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5651 {
5652   DM_Plex *mesh = (DM_Plex *)dm->data;
5653 
5654   PetscFunctionBegin;
5655   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5656   if (cones) *cones = mesh->cones;
5657   PetscFunctionReturn(PETSC_SUCCESS);
5658 }
5659 
5660 /*@C
5661   DMPlexGetConeOrientations - Return cone orientation data
5662 
5663   Not Collective
5664 
5665   Input Parameter:
5666 . dm - The `DMPLEX` object
5667 
5668   Output Parameter:
5669 . coneOrientations - The array of cone orientations for all points
5670 
5671   Level: developer
5672 
5673   Notes:
5674   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5675 
5676   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5677 
5678 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5679 @*/
5680 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5681 {
5682   DM_Plex *mesh = (DM_Plex *)dm->data;
5683 
5684   PetscFunctionBegin;
5685   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5686   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5687   PetscFunctionReturn(PETSC_SUCCESS);
5688 }
5689 
5690 /******************************** FEM Support **********************************/
5691 
5692 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5693 {
5694   PetscInt depth;
5695 
5696   PetscFunctionBegin;
5697   PetscCall(DMPlexGetDepth(plex, &depth));
5698   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5699   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5700   PetscFunctionReturn(PETSC_SUCCESS);
5701 }
5702 
5703 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5704 {
5705   PetscInt depth;
5706 
5707   PetscFunctionBegin;
5708   PetscCall(DMPlexGetDepth(plex, &depth));
5709   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5710   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5711   PetscFunctionReturn(PETSC_SUCCESS);
5712 }
5713 
5714 /*
5715  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5716  representing a line in the section.
5717 */
5718 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5719 {
5720   PetscObject  obj;
5721   PetscClassId id;
5722   PetscFE      fe = NULL;
5723 
5724   PetscFunctionBeginHot;
5725   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5726   PetscCall(DMGetField(dm, field, NULL, &obj));
5727   PetscCall(PetscObjectGetClassId(obj, &id));
5728   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5729 
5730   if (!fe) {
5731     /* Assume the full interpolated mesh is in the chart; lines in particular */
5732     /* An order k SEM disc has k-1 dofs on an edge */
5733     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5734     *k = *k / *Nc + 1;
5735   } else {
5736     PetscInt       dual_space_size, dim;
5737     PetscDualSpace dsp;
5738 
5739     PetscCall(DMGetDimension(dm, &dim));
5740     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5741     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5742     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5743     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5744     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5745   }
5746   PetscFunctionReturn(PETSC_SUCCESS);
5747 }
5748 
5749 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5750 {
5751   PetscFunctionBeginHot;
5752   if (tensor) {
5753     *dof = PetscPowInt(k + 1, dim);
5754   } else {
5755     switch (dim) {
5756     case 1:
5757       *dof = k + 1;
5758       break;
5759     case 2:
5760       *dof = ((k + 1) * (k + 2)) / 2;
5761       break;
5762     case 3:
5763       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5764       break;
5765     default:
5766       *dof = 0;
5767     }
5768   }
5769   PetscFunctionReturn(PETSC_SUCCESS);
5770 }
5771 
5772 /*@
5773 
5774   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5775   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5776   section provided (or the section of the `DM`).
5777 
5778   Input Parameters:
5779 + dm      - The `DM`
5780 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5781 - section - The `PetscSection` to reorder, or `NULL` for the default section
5782 
5783   Example:
5784   A typical interpolated single-quad mesh might order points as
5785 .vb
5786   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5787 
5788   v4 -- e6 -- v3
5789   |           |
5790   e7    c0    e8
5791   |           |
5792   v1 -- e5 -- v2
5793 .ve
5794 
5795   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5796   dofs in the order of points, e.g.,
5797 .vb
5798     c0 -> [0,1,2,3]
5799     v1 -> [4]
5800     ...
5801     e5 -> [8, 9]
5802 .ve
5803 
5804   which corresponds to the dofs
5805 .vb
5806     6   10  11  7
5807     13  2   3   15
5808     12  0   1   14
5809     4   8   9   5
5810 .ve
5811 
5812   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5813 .vb
5814   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5815 .ve
5816 
5817   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5818 .vb
5819    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5820 .ve
5821 
5822   Level: developer
5823 
5824   Notes:
5825   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5826   degree of the basis.
5827 
5828   This is required to run with libCEED.
5829 
5830 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5831 @*/
5832 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5833 {
5834   DMLabel   label;
5835   PetscInt  dim, depth = -1, eStart = -1, Nf;
5836   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5837 
5838   PetscFunctionBegin;
5839   PetscCall(DMGetDimension(dm, &dim));
5840   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5841   if (point < 0) {
5842     PetscInt sStart, sEnd;
5843 
5844     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5845     point = sEnd - sStart ? sStart : point;
5846   }
5847   PetscCall(DMPlexGetDepthLabel(dm, &label));
5848   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5849   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5850   if (depth == 1) {
5851     eStart = point;
5852   } else if (depth == dim) {
5853     const PetscInt *cone;
5854 
5855     PetscCall(DMPlexGetCone(dm, point, &cone));
5856     if (dim == 2) eStart = cone[0];
5857     else if (dim == 3) {
5858       const PetscInt *cone2;
5859       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5860       eStart = cone2[0];
5861     } 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);
5862   } 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);
5863 
5864   PetscCall(PetscSectionGetNumFields(section, &Nf));
5865   for (PetscInt d = 1; d <= dim; d++) {
5866     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5867     PetscInt *perm;
5868 
5869     for (f = 0; f < Nf; ++f) {
5870       PetscInt dof;
5871 
5872       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5873       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5874       if (!continuous && d < dim) continue;
5875       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5876       size += dof * Nc;
5877     }
5878     PetscCall(PetscMalloc1(size, &perm));
5879     for (f = 0; f < Nf; ++f) {
5880       switch (d) {
5881       case 1:
5882         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5883         if (!continuous && d < dim) continue;
5884         /*
5885          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5886          We want              [ vtx0; edge of length k-1; vtx1 ]
5887          */
5888         if (continuous) {
5889           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5890           for (i = 0; i < k - 1; i++)
5891             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5892           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5893           foffset = offset;
5894         } else {
5895           PetscInt dof;
5896 
5897           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5898           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5899           foffset = offset;
5900         }
5901         break;
5902       case 2:
5903         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5904         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5905         if (!continuous && d < dim) continue;
5906         /* The SEM order is
5907 
5908          v_lb, {e_b}, v_rb,
5909          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5910          v_lt, reverse {e_t}, v_rt
5911          */
5912         if (continuous) {
5913           const PetscInt of   = 0;
5914           const PetscInt oeb  = of + PetscSqr(k - 1);
5915           const PetscInt oer  = oeb + (k - 1);
5916           const PetscInt oet  = oer + (k - 1);
5917           const PetscInt oel  = oet + (k - 1);
5918           const PetscInt ovlb = oel + (k - 1);
5919           const PetscInt ovrb = ovlb + 1;
5920           const PetscInt ovrt = ovrb + 1;
5921           const PetscInt ovlt = ovrt + 1;
5922           PetscInt       o;
5923 
5924           /* bottom */
5925           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5926           for (o = oeb; o < oer; ++o)
5927             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5928           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5929           /* middle */
5930           for (i = 0; i < k - 1; ++i) {
5931             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5932             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5933               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5934             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5935           }
5936           /* top */
5937           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5938           for (o = oel - 1; o >= oet; --o)
5939             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5940           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5941           foffset = offset;
5942         } else {
5943           PetscInt dof;
5944 
5945           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5946           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5947           foffset = offset;
5948         }
5949         break;
5950       case 3:
5951         /* The original hex closure is
5952 
5953          {c,
5954          f_b, f_t, f_f, f_b, f_r, f_l,
5955          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5956          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5957          */
5958         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5959         if (!continuous && d < dim) continue;
5960         /* The SEM order is
5961          Bottom Slice
5962          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5963          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5964          v_blb, {e_bb}, v_brb,
5965 
5966          Middle Slice (j)
5967          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5968          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5969          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5970 
5971          Top Slice
5972          v_tlf, {e_tf}, v_trf,
5973          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5974          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5975          */
5976         if (continuous) {
5977           const PetscInt oc    = 0;
5978           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5979           const PetscInt oft   = ofb + PetscSqr(k - 1);
5980           const PetscInt off   = oft + PetscSqr(k - 1);
5981           const PetscInt ofk   = off + PetscSqr(k - 1);
5982           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5983           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5984           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5985           const PetscInt oebb  = oebl + (k - 1);
5986           const PetscInt oebr  = oebb + (k - 1);
5987           const PetscInt oebf  = oebr + (k - 1);
5988           const PetscInt oetf  = oebf + (k - 1);
5989           const PetscInt oetr  = oetf + (k - 1);
5990           const PetscInt oetb  = oetr + (k - 1);
5991           const PetscInt oetl  = oetb + (k - 1);
5992           const PetscInt oerf  = oetl + (k - 1);
5993           const PetscInt oelf  = oerf + (k - 1);
5994           const PetscInt oelb  = oelf + (k - 1);
5995           const PetscInt oerb  = oelb + (k - 1);
5996           const PetscInt ovblf = oerb + (k - 1);
5997           const PetscInt ovblb = ovblf + 1;
5998           const PetscInt ovbrb = ovblb + 1;
5999           const PetscInt ovbrf = ovbrb + 1;
6000           const PetscInt ovtlf = ovbrf + 1;
6001           const PetscInt ovtrf = ovtlf + 1;
6002           const PetscInt ovtrb = ovtrf + 1;
6003           const PetscInt ovtlb = ovtrb + 1;
6004           PetscInt       o, n;
6005 
6006           /* Bottom Slice */
6007           /*   bottom */
6008           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6009           for (o = oetf - 1; o >= oebf; --o)
6010             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6011           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6012           /*   middle */
6013           for (i = 0; i < k - 1; ++i) {
6014             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6015             for (n = 0; n < k - 1; ++n) {
6016               o = ofb + n * (k - 1) + i;
6017               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6018             }
6019             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6020           }
6021           /*   top */
6022           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6023           for (o = oebb; o < oebr; ++o)
6024             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6025           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6026 
6027           /* Middle Slice */
6028           for (j = 0; j < k - 1; ++j) {
6029             /*   bottom */
6030             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6031             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6032               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6033             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6034             /*   middle */
6035             for (i = 0; i < k - 1; ++i) {
6036               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6037               for (n = 0; n < k - 1; ++n)
6038                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6039               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6040             }
6041             /*   top */
6042             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6043             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6044               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6045             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6046           }
6047 
6048           /* Top Slice */
6049           /*   bottom */
6050           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6051           for (o = oetf; o < oetr; ++o)
6052             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6053           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6054           /*   middle */
6055           for (i = 0; i < k - 1; ++i) {
6056             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6057             for (n = 0; n < k - 1; ++n)
6058               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6059             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6060           }
6061           /*   top */
6062           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6063           for (o = oetl - 1; o >= oetb; --o)
6064             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6065           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6066 
6067           foffset = offset;
6068         } else {
6069           PetscInt dof;
6070 
6071           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6072           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6073           foffset = offset;
6074         }
6075         break;
6076       default:
6077         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6078       }
6079     }
6080     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6081     /* Check permutation */
6082     {
6083       PetscInt *check;
6084 
6085       PetscCall(PetscMalloc1(size, &check));
6086       for (i = 0; i < size; ++i) {
6087         check[i] = -1;
6088         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6089       }
6090       for (i = 0; i < size; ++i) check[perm[i]] = i;
6091       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6092       PetscCall(PetscFree(check));
6093     }
6094     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6095     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6096       PetscInt *loc_perm;
6097       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6098       for (PetscInt i = 0; i < size; i++) {
6099         loc_perm[i]        = perm[i];
6100         loc_perm[size + i] = size + perm[i];
6101       }
6102       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6103     }
6104   }
6105   PetscFunctionReturn(PETSC_SUCCESS);
6106 }
6107 
6108 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6109 {
6110   PetscDS  prob;
6111   PetscInt depth, Nf, h;
6112   DMLabel  label;
6113 
6114   PetscFunctionBeginHot;
6115   PetscCall(DMGetDS(dm, &prob));
6116   Nf      = prob->Nf;
6117   label   = dm->depthLabel;
6118   *dspace = NULL;
6119   if (field < Nf) {
6120     PetscObject disc = prob->disc[field];
6121 
6122     if (disc->classid == PETSCFE_CLASSID) {
6123       PetscDualSpace dsp;
6124 
6125       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6126       PetscCall(DMLabelGetNumValues(label, &depth));
6127       PetscCall(DMLabelGetValue(label, point, &h));
6128       h = depth - 1 - h;
6129       if (h) {
6130         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6131       } else {
6132         *dspace = dsp;
6133       }
6134     }
6135   }
6136   PetscFunctionReturn(PETSC_SUCCESS);
6137 }
6138 
6139 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6140 {
6141   PetscScalar       *array;
6142   const PetscScalar *vArray;
6143   const PetscInt    *cone, *coneO;
6144   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6145 
6146   PetscFunctionBeginHot;
6147   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6148   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6149   PetscCall(DMPlexGetCone(dm, point, &cone));
6150   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6151   if (!values || !*values) {
6152     if ((point >= pStart) && (point < pEnd)) {
6153       PetscInt dof;
6154 
6155       PetscCall(PetscSectionGetDof(section, point, &dof));
6156       size += dof;
6157     }
6158     for (p = 0; p < numPoints; ++p) {
6159       const PetscInt cp = cone[p];
6160       PetscInt       dof;
6161 
6162       if ((cp < pStart) || (cp >= pEnd)) continue;
6163       PetscCall(PetscSectionGetDof(section, cp, &dof));
6164       size += dof;
6165     }
6166     if (!values) {
6167       if (csize) *csize = size;
6168       PetscFunctionReturn(PETSC_SUCCESS);
6169     }
6170     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6171   } else {
6172     array = *values;
6173   }
6174   size = 0;
6175   PetscCall(VecGetArrayRead(v, &vArray));
6176   if ((point >= pStart) && (point < pEnd)) {
6177     PetscInt           dof, off, d;
6178     const PetscScalar *varr;
6179 
6180     PetscCall(PetscSectionGetDof(section, point, &dof));
6181     PetscCall(PetscSectionGetOffset(section, point, &off));
6182     varr = PetscSafePointerPlusOffset(vArray, off);
6183     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6184     size += dof;
6185   }
6186   for (p = 0; p < numPoints; ++p) {
6187     const PetscInt     cp = cone[p];
6188     PetscInt           o  = coneO[p];
6189     PetscInt           dof, off, d;
6190     const PetscScalar *varr;
6191 
6192     if ((cp < pStart) || (cp >= pEnd)) continue;
6193     PetscCall(PetscSectionGetDof(section, cp, &dof));
6194     PetscCall(PetscSectionGetOffset(section, cp, &off));
6195     varr = PetscSafePointerPlusOffset(vArray, off);
6196     if (o >= 0) {
6197       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6198     } else {
6199       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6200     }
6201     size += dof;
6202   }
6203   PetscCall(VecRestoreArrayRead(v, &vArray));
6204   if (!*values) {
6205     if (csize) *csize = size;
6206     *values = array;
6207   } else {
6208     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6209     *csize = size;
6210   }
6211   PetscFunctionReturn(PETSC_SUCCESS);
6212 }
6213 
6214 /* Compress out points not in the section */
6215 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6216 {
6217   const PetscInt np = *numPoints;
6218   PetscInt       pStart, pEnd, p, q;
6219 
6220   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6221   for (p = 0, q = 0; p < np; ++p) {
6222     const PetscInt r = points[p * 2];
6223     if ((r >= pStart) && (r < pEnd)) {
6224       points[q * 2]     = r;
6225       points[q * 2 + 1] = points[p * 2 + 1];
6226       ++q;
6227     }
6228   }
6229   *numPoints = q;
6230   return PETSC_SUCCESS;
6231 }
6232 
6233 /* Compressed closure does not apply closure permutation */
6234 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6235 {
6236   const PetscInt *cla = NULL;
6237   PetscInt        np, *pts = NULL;
6238 
6239   PetscFunctionBeginHot;
6240   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6241   if (!ornt && *clPoints) {
6242     PetscInt dof, off;
6243 
6244     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6245     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6246     PetscCall(ISGetIndices(*clPoints, &cla));
6247     np  = dof / 2;
6248     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6249   } else {
6250     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6251     PetscCall(CompressPoints_Private(section, &np, pts));
6252   }
6253   *numPoints = np;
6254   *points    = pts;
6255   *clp       = cla;
6256   PetscFunctionReturn(PETSC_SUCCESS);
6257 }
6258 
6259 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6260 {
6261   PetscFunctionBeginHot;
6262   if (!*clPoints) {
6263     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6264   } else {
6265     PetscCall(ISRestoreIndices(*clPoints, clp));
6266   }
6267   *numPoints = 0;
6268   *points    = NULL;
6269   *clSec     = NULL;
6270   *clPoints  = NULL;
6271   *clp       = NULL;
6272   PetscFunctionReturn(PETSC_SUCCESS);
6273 }
6274 
6275 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6276 {
6277   PetscInt            offset = 0, p;
6278   const PetscInt    **perms  = NULL;
6279   const PetscScalar **flips  = NULL;
6280 
6281   PetscFunctionBeginHot;
6282   *size = 0;
6283   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6284   for (p = 0; p < numPoints; p++) {
6285     const PetscInt     point = points[2 * p];
6286     const PetscInt    *perm  = perms ? perms[p] : NULL;
6287     const PetscScalar *flip  = flips ? flips[p] : NULL;
6288     PetscInt           dof, off, d;
6289     const PetscScalar *varr;
6290 
6291     PetscCall(PetscSectionGetDof(section, point, &dof));
6292     PetscCall(PetscSectionGetOffset(section, point, &off));
6293     varr = PetscSafePointerPlusOffset(vArray, off);
6294     if (clperm) {
6295       if (perm) {
6296         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6297       } else {
6298         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6299       }
6300       if (flip) {
6301         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6302       }
6303     } else {
6304       if (perm) {
6305         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6306       } else {
6307         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6308       }
6309       if (flip) {
6310         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6311       }
6312     }
6313     offset += dof;
6314   }
6315   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6316   *size = offset;
6317   PetscFunctionReturn(PETSC_SUCCESS);
6318 }
6319 
6320 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[])
6321 {
6322   PetscInt offset = 0, f;
6323 
6324   PetscFunctionBeginHot;
6325   *size = 0;
6326   for (f = 0; f < numFields; ++f) {
6327     PetscInt            p;
6328     const PetscInt    **perms = NULL;
6329     const PetscScalar **flips = NULL;
6330 
6331     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6332     for (p = 0; p < numPoints; p++) {
6333       const PetscInt     point = points[2 * p];
6334       PetscInt           fdof, foff, b;
6335       const PetscScalar *varr;
6336       const PetscInt    *perm = perms ? perms[p] : NULL;
6337       const PetscScalar *flip = flips ? flips[p] : NULL;
6338 
6339       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6340       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6341       varr = &vArray[foff];
6342       if (clperm) {
6343         if (perm) {
6344           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6345         } else {
6346           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6347         }
6348         if (flip) {
6349           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6350         }
6351       } else {
6352         if (perm) {
6353           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6354         } else {
6355           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6356         }
6357         if (flip) {
6358           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6359         }
6360       }
6361       offset += fdof;
6362     }
6363     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6364   }
6365   *size = offset;
6366   PetscFunctionReturn(PETSC_SUCCESS);
6367 }
6368 
6369 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6370 {
6371   PetscSection    clSection;
6372   IS              clPoints;
6373   PetscInt       *points = NULL;
6374   const PetscInt *clp, *perm = NULL;
6375   PetscInt        depth, numFields, numPoints, asize;
6376 
6377   PetscFunctionBeginHot;
6378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6379   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6380   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6381   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6382   PetscCall(DMPlexGetDepth(dm, &depth));
6383   PetscCall(PetscSectionGetNumFields(section, &numFields));
6384   if (depth == 1 && numFields < 2) {
6385     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6386     PetscFunctionReturn(PETSC_SUCCESS);
6387   }
6388   /* Get points */
6389   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6390   /* Get sizes */
6391   asize = 0;
6392   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6393     PetscInt dof;
6394     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6395     asize += dof;
6396   }
6397   if (values) {
6398     const PetscScalar *vArray;
6399     PetscInt           size;
6400 
6401     if (*values) {
6402       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);
6403     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6404     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6405     PetscCall(VecGetArrayRead(v, &vArray));
6406     /* Get values */
6407     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6408     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6409     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6410     /* Cleanup array */
6411     PetscCall(VecRestoreArrayRead(v, &vArray));
6412   }
6413   if (csize) *csize = asize;
6414   /* Cleanup points */
6415   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6416   PetscFunctionReturn(PETSC_SUCCESS);
6417 }
6418 
6419 /*@C
6420   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6421 
6422   Not collective
6423 
6424   Input Parameters:
6425 + dm      - The `DM`
6426 . section - The section describing the layout in `v`, or `NULL` to use the default section
6427 . v       - The local vector
6428 - point   - The point in the `DM`
6429 
6430   Input/Output Parameters:
6431 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6432 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6433            if the user provided `NULL`, it is a borrowed array and should not be freed
6434 
6435   Level: intermediate
6436 
6437   Notes:
6438   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6439   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6440   assembly function, and a user may already have allocated storage for this operation.
6441 
6442   A typical use could be
6443 .vb
6444    values = NULL;
6445    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6446    for (cl = 0; cl < clSize; ++cl) {
6447      <Compute on closure>
6448    }
6449    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6450 .ve
6451   or
6452 .vb
6453    PetscMalloc1(clMaxSize, &values);
6454    for (p = pStart; p < pEnd; ++p) {
6455      clSize = clMaxSize;
6456      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6457      for (cl = 0; cl < clSize; ++cl) {
6458        <Compute on closure>
6459      }
6460    }
6461    PetscFree(values);
6462 .ve
6463 
6464   Fortran Notes:
6465   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6466 
6467 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6468 @*/
6469 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6470 {
6471   PetscFunctionBeginHot;
6472   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6473   PetscFunctionReturn(PETSC_SUCCESS);
6474 }
6475 
6476 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6477 {
6478   DMLabel            depthLabel;
6479   PetscSection       clSection;
6480   IS                 clPoints;
6481   PetscScalar       *array;
6482   const PetscScalar *vArray;
6483   PetscInt          *points = NULL;
6484   const PetscInt    *clp, *perm = NULL;
6485   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6486 
6487   PetscFunctionBeginHot;
6488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6489   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6490   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6491   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6492   PetscCall(DMPlexGetDepth(dm, &mdepth));
6493   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6494   PetscCall(PetscSectionGetNumFields(section, &numFields));
6495   if (mdepth == 1 && numFields < 2) {
6496     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6497     PetscFunctionReturn(PETSC_SUCCESS);
6498   }
6499   /* Get points */
6500   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6501   for (clsize = 0, p = 0; p < Np; p++) {
6502     PetscInt dof;
6503     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6504     clsize += dof;
6505   }
6506   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6507   /* Filter points */
6508   for (p = 0; p < numPoints * 2; p += 2) {
6509     PetscInt dep;
6510 
6511     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6512     if (dep != depth) continue;
6513     points[Np * 2 + 0] = points[p];
6514     points[Np * 2 + 1] = points[p + 1];
6515     ++Np;
6516   }
6517   /* Get array */
6518   if (!values || !*values) {
6519     PetscInt asize = 0, dof;
6520 
6521     for (p = 0; p < Np * 2; p += 2) {
6522       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6523       asize += dof;
6524     }
6525     if (!values) {
6526       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6527       if (csize) *csize = asize;
6528       PetscFunctionReturn(PETSC_SUCCESS);
6529     }
6530     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6531   } else {
6532     array = *values;
6533   }
6534   PetscCall(VecGetArrayRead(v, &vArray));
6535   /* Get values */
6536   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6537   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6538   /* Cleanup points */
6539   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6540   /* Cleanup array */
6541   PetscCall(VecRestoreArrayRead(v, &vArray));
6542   if (!*values) {
6543     if (csize) *csize = size;
6544     *values = array;
6545   } else {
6546     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6547     *csize = size;
6548   }
6549   PetscFunctionReturn(PETSC_SUCCESS);
6550 }
6551 
6552 /*@C
6553   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6554 
6555   Not collective
6556 
6557   Input Parameters:
6558 + dm      - The `DM`
6559 . section - The section describing the layout in `v`, or `NULL` to use the default section
6560 . v       - The local vector
6561 . point   - The point in the `DM`
6562 . csize   - The number of values in the closure, or `NULL`
6563 - values  - The array of values, which is a borrowed array and should not be freed
6564 
6565   Level: intermediate
6566 
6567   Note:
6568   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6569 
6570   Fortran Notes:
6571   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6572 
6573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6574 @*/
6575 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6576 {
6577   PetscInt size = 0;
6578 
6579   PetscFunctionBegin;
6580   /* Should work without recalculating size */
6581   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6582   *values = NULL;
6583   PetscFunctionReturn(PETSC_SUCCESS);
6584 }
6585 
6586 static inline void add(PetscScalar *x, PetscScalar y)
6587 {
6588   *x += y;
6589 }
6590 static inline void insert(PetscScalar *x, PetscScalar y)
6591 {
6592   *x = y;
6593 }
6594 
6595 static inline PetscErrorCode updatePoint_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar *, PetscScalar), PetscBool setBC, const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6596 {
6597   PetscInt        cdof;  /* The number of constraints on this point */
6598   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6599   PetscScalar    *a;
6600   PetscInt        off, cind = 0, k;
6601 
6602   PetscFunctionBegin;
6603   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6604   PetscCall(PetscSectionGetOffset(section, point, &off));
6605   a = &array[off];
6606   if (!cdof || setBC) {
6607     if (clperm) {
6608       if (perm) {
6609         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6610       } else {
6611         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6612       }
6613     } else {
6614       if (perm) {
6615         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6616       } else {
6617         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6618       }
6619     }
6620   } else {
6621     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6622     if (clperm) {
6623       if (perm) {
6624         for (k = 0; k < dof; ++k) {
6625           if ((cind < cdof) && (k == cdofs[cind])) {
6626             ++cind;
6627             continue;
6628           }
6629           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6630         }
6631       } else {
6632         for (k = 0; k < dof; ++k) {
6633           if ((cind < cdof) && (k == cdofs[cind])) {
6634             ++cind;
6635             continue;
6636           }
6637           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6638         }
6639       }
6640     } else {
6641       if (perm) {
6642         for (k = 0; k < dof; ++k) {
6643           if ((cind < cdof) && (k == cdofs[cind])) {
6644             ++cind;
6645             continue;
6646           }
6647           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6648         }
6649       } else {
6650         for (k = 0; k < dof; ++k) {
6651           if ((cind < cdof) && (k == cdofs[cind])) {
6652             ++cind;
6653             continue;
6654           }
6655           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6656         }
6657       }
6658     }
6659   }
6660   PetscFunctionReturn(PETSC_SUCCESS);
6661 }
6662 
6663 static inline PetscErrorCode updatePointBC_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar *, PetscScalar), const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6664 {
6665   PetscInt        cdof;  /* The number of constraints on this point */
6666   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6667   PetscScalar    *a;
6668   PetscInt        off, cind = 0, k;
6669 
6670   PetscFunctionBegin;
6671   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6672   PetscCall(PetscSectionGetOffset(section, point, &off));
6673   a = &array[off];
6674   if (cdof) {
6675     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6676     if (clperm) {
6677       if (perm) {
6678         for (k = 0; k < dof; ++k) {
6679           if ((cind < cdof) && (k == cdofs[cind])) {
6680             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6681             cind++;
6682           }
6683         }
6684       } else {
6685         for (k = 0; k < dof; ++k) {
6686           if ((cind < cdof) && (k == cdofs[cind])) {
6687             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6688             cind++;
6689           }
6690         }
6691       }
6692     } else {
6693       if (perm) {
6694         for (k = 0; k < dof; ++k) {
6695           if ((cind < cdof) && (k == cdofs[cind])) {
6696             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6697             cind++;
6698           }
6699         }
6700       } else {
6701         for (k = 0; k < dof; ++k) {
6702           if ((cind < cdof) && (k == cdofs[cind])) {
6703             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6704             cind++;
6705           }
6706         }
6707       }
6708     }
6709   }
6710   PetscFunctionReturn(PETSC_SUCCESS);
6711 }
6712 
6713 static inline PetscErrorCode updatePointFields_private(PetscSection section, PetscInt point, const PetscInt *perm, const PetscScalar *flip, PetscInt f, void (*fuse)(PetscScalar *, PetscScalar), PetscBool setBC, const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6714 {
6715   PetscScalar    *a;
6716   PetscInt        fdof, foff, fcdof, foffset = *offset;
6717   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6718   PetscInt        cind = 0, b;
6719 
6720   PetscFunctionBegin;
6721   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6722   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6723   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6724   a = &array[foff];
6725   if (!fcdof || setBC) {
6726     if (clperm) {
6727       if (perm) {
6728         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6729       } else {
6730         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6731       }
6732     } else {
6733       if (perm) {
6734         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6735       } else {
6736         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6737       }
6738     }
6739   } else {
6740     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6741     if (clperm) {
6742       if (perm) {
6743         for (b = 0; b < fdof; b++) {
6744           if ((cind < fcdof) && (b == fcdofs[cind])) {
6745             ++cind;
6746             continue;
6747           }
6748           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6749         }
6750       } else {
6751         for (b = 0; b < fdof; b++) {
6752           if ((cind < fcdof) && (b == fcdofs[cind])) {
6753             ++cind;
6754             continue;
6755           }
6756           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6757         }
6758       }
6759     } else {
6760       if (perm) {
6761         for (b = 0; b < fdof; b++) {
6762           if ((cind < fcdof) && (b == fcdofs[cind])) {
6763             ++cind;
6764             continue;
6765           }
6766           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6767         }
6768       } else {
6769         for (b = 0; b < fdof; b++) {
6770           if ((cind < fcdof) && (b == fcdofs[cind])) {
6771             ++cind;
6772             continue;
6773           }
6774           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6775         }
6776       }
6777     }
6778   }
6779   *offset += fdof;
6780   PetscFunctionReturn(PETSC_SUCCESS);
6781 }
6782 
6783 static inline PetscErrorCode updatePointFieldsBC_private(PetscSection section, PetscInt point, const PetscInt perm[], const PetscScalar flip[], PetscInt f, PetscInt Ncc, const PetscInt comps[], void (*fuse)(PetscScalar *, PetscScalar), const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6784 {
6785   PetscScalar    *a;
6786   PetscInt        fdof, foff, fcdof, foffset = *offset;
6787   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6788   PetscInt        Nc, cind = 0, ncind = 0, b;
6789   PetscBool       ncSet, fcSet;
6790 
6791   PetscFunctionBegin;
6792   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6793   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6794   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6795   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6796   a = &array[foff];
6797   if (fcdof) {
6798     /* We just override fcdof and fcdofs with Ncc and comps */
6799     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6800     if (clperm) {
6801       if (perm) {
6802         if (comps) {
6803           for (b = 0; b < fdof; b++) {
6804             ncSet = fcSet = PETSC_FALSE;
6805             if (b % Nc == comps[ncind]) {
6806               ncind = (ncind + 1) % Ncc;
6807               ncSet = PETSC_TRUE;
6808             }
6809             if ((cind < fcdof) && (b == fcdofs[cind])) {
6810               ++cind;
6811               fcSet = PETSC_TRUE;
6812             }
6813             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6814           }
6815         } else {
6816           for (b = 0; b < fdof; b++) {
6817             if ((cind < fcdof) && (b == fcdofs[cind])) {
6818               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6819               ++cind;
6820             }
6821           }
6822         }
6823       } else {
6824         if (comps) {
6825           for (b = 0; b < fdof; b++) {
6826             ncSet = fcSet = PETSC_FALSE;
6827             if (b % Nc == comps[ncind]) {
6828               ncind = (ncind + 1) % Ncc;
6829               ncSet = PETSC_TRUE;
6830             }
6831             if ((cind < fcdof) && (b == fcdofs[cind])) {
6832               ++cind;
6833               fcSet = PETSC_TRUE;
6834             }
6835             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6836           }
6837         } else {
6838           for (b = 0; b < fdof; b++) {
6839             if ((cind < fcdof) && (b == fcdofs[cind])) {
6840               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6841               ++cind;
6842             }
6843           }
6844         }
6845       }
6846     } else {
6847       if (perm) {
6848         if (comps) {
6849           for (b = 0; b < fdof; b++) {
6850             ncSet = fcSet = PETSC_FALSE;
6851             if (b % Nc == comps[ncind]) {
6852               ncind = (ncind + 1) % Ncc;
6853               ncSet = PETSC_TRUE;
6854             }
6855             if ((cind < fcdof) && (b == fcdofs[cind])) {
6856               ++cind;
6857               fcSet = PETSC_TRUE;
6858             }
6859             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6860           }
6861         } else {
6862           for (b = 0; b < fdof; b++) {
6863             if ((cind < fcdof) && (b == fcdofs[cind])) {
6864               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6865               ++cind;
6866             }
6867           }
6868         }
6869       } else {
6870         if (comps) {
6871           for (b = 0; b < fdof; b++) {
6872             ncSet = fcSet = PETSC_FALSE;
6873             if (b % Nc == comps[ncind]) {
6874               ncind = (ncind + 1) % Ncc;
6875               ncSet = PETSC_TRUE;
6876             }
6877             if ((cind < fcdof) && (b == fcdofs[cind])) {
6878               ++cind;
6879               fcSet = PETSC_TRUE;
6880             }
6881             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6882           }
6883         } else {
6884           for (b = 0; b < fdof; b++) {
6885             if ((cind < fcdof) && (b == fcdofs[cind])) {
6886               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6887               ++cind;
6888             }
6889           }
6890         }
6891       }
6892     }
6893   }
6894   *offset += fdof;
6895   PetscFunctionReturn(PETSC_SUCCESS);
6896 }
6897 
6898 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6899 {
6900   PetscScalar    *array;
6901   const PetscInt *cone, *coneO;
6902   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6903 
6904   PetscFunctionBeginHot;
6905   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6906   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6907   PetscCall(DMPlexGetCone(dm, point, &cone));
6908   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6909   PetscCall(VecGetArray(v, &array));
6910   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6911     const PetscInt cp = !p ? point : cone[p - 1];
6912     const PetscInt o  = !p ? 0 : coneO[p - 1];
6913 
6914     if ((cp < pStart) || (cp >= pEnd)) {
6915       dof = 0;
6916       continue;
6917     }
6918     PetscCall(PetscSectionGetDof(section, cp, &dof));
6919     /* ADD_VALUES */
6920     {
6921       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6922       PetscScalar    *a;
6923       PetscInt        cdof, coff, cind = 0, k;
6924 
6925       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6926       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6927       a = &array[coff];
6928       if (!cdof) {
6929         if (o >= 0) {
6930           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6931         } else {
6932           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6933         }
6934       } else {
6935         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6936         if (o >= 0) {
6937           for (k = 0; k < dof; ++k) {
6938             if ((cind < cdof) && (k == cdofs[cind])) {
6939               ++cind;
6940               continue;
6941             }
6942             a[k] += values[off + k];
6943           }
6944         } else {
6945           for (k = 0; k < dof; ++k) {
6946             if ((cind < cdof) && (k == cdofs[cind])) {
6947               ++cind;
6948               continue;
6949             }
6950             a[k] += values[off + dof - k - 1];
6951           }
6952         }
6953       }
6954     }
6955   }
6956   PetscCall(VecRestoreArray(v, &array));
6957   PetscFunctionReturn(PETSC_SUCCESS);
6958 }
6959 
6960 /*@C
6961   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6962 
6963   Not collective
6964 
6965   Input Parameters:
6966 + dm      - The `DM`
6967 . section - The section describing the layout in `v`, or `NULL` to use the default section
6968 . v       - The local vector
6969 . point   - The point in the `DM`
6970 . values  - The array of values
6971 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6972          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6973 
6974   Level: intermediate
6975 
6976 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6977 @*/
6978 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6979 {
6980   PetscSection    clSection;
6981   IS              clPoints;
6982   PetscScalar    *array;
6983   PetscInt       *points = NULL;
6984   const PetscInt *clp, *clperm = NULL;
6985   PetscInt        depth, numFields, numPoints, p, clsize;
6986 
6987   PetscFunctionBeginHot;
6988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6989   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6990   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6991   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6992   PetscCall(DMPlexGetDepth(dm, &depth));
6993   PetscCall(PetscSectionGetNumFields(section, &numFields));
6994   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6995     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6996     PetscFunctionReturn(PETSC_SUCCESS);
6997   }
6998   /* Get points */
6999   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7000   for (clsize = 0, p = 0; p < numPoints; p++) {
7001     PetscInt dof;
7002     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7003     clsize += dof;
7004   }
7005   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7006   /* Get array */
7007   PetscCall(VecGetArray(v, &array));
7008   /* Get values */
7009   if (numFields > 0) {
7010     PetscInt offset = 0, f;
7011     for (f = 0; f < numFields; ++f) {
7012       const PetscInt    **perms = NULL;
7013       const PetscScalar **flips = NULL;
7014 
7015       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7016       switch (mode) {
7017       case INSERT_VALUES:
7018         for (p = 0; p < numPoints; p++) {
7019           const PetscInt     point = points[2 * p];
7020           const PetscInt    *perm  = perms ? perms[p] : NULL;
7021           const PetscScalar *flip  = flips ? flips[p] : NULL;
7022           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7023         }
7024         break;
7025       case INSERT_ALL_VALUES:
7026         for (p = 0; p < numPoints; p++) {
7027           const PetscInt     point = points[2 * p];
7028           const PetscInt    *perm  = perms ? perms[p] : NULL;
7029           const PetscScalar *flip  = flips ? flips[p] : NULL;
7030           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7031         }
7032         break;
7033       case INSERT_BC_VALUES:
7034         for (p = 0; p < numPoints; p++) {
7035           const PetscInt     point = points[2 * p];
7036           const PetscInt    *perm  = perms ? perms[p] : NULL;
7037           const PetscScalar *flip  = flips ? flips[p] : NULL;
7038           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7039         }
7040         break;
7041       case ADD_VALUES:
7042         for (p = 0; p < numPoints; p++) {
7043           const PetscInt     point = points[2 * p];
7044           const PetscInt    *perm  = perms ? perms[p] : NULL;
7045           const PetscScalar *flip  = flips ? flips[p] : NULL;
7046           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7047         }
7048         break;
7049       case ADD_ALL_VALUES:
7050         for (p = 0; p < numPoints; p++) {
7051           const PetscInt     point = points[2 * p];
7052           const PetscInt    *perm  = perms ? perms[p] : NULL;
7053           const PetscScalar *flip  = flips ? flips[p] : NULL;
7054           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7055         }
7056         break;
7057       case ADD_BC_VALUES:
7058         for (p = 0; p < numPoints; p++) {
7059           const PetscInt     point = points[2 * p];
7060           const PetscInt    *perm  = perms ? perms[p] : NULL;
7061           const PetscScalar *flip  = flips ? flips[p] : NULL;
7062           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7063         }
7064         break;
7065       default:
7066         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7067       }
7068       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7069     }
7070   } else {
7071     PetscInt            dof, off;
7072     const PetscInt    **perms = NULL;
7073     const PetscScalar **flips = NULL;
7074 
7075     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7076     switch (mode) {
7077     case INSERT_VALUES:
7078       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7079         const PetscInt     point = points[2 * p];
7080         const PetscInt    *perm  = perms ? perms[p] : NULL;
7081         const PetscScalar *flip  = flips ? flips[p] : NULL;
7082         PetscCall(PetscSectionGetDof(section, point, &dof));
7083         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7084       }
7085       break;
7086     case INSERT_ALL_VALUES:
7087       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7088         const PetscInt     point = points[2 * p];
7089         const PetscInt    *perm  = perms ? perms[p] : NULL;
7090         const PetscScalar *flip  = flips ? flips[p] : NULL;
7091         PetscCall(PetscSectionGetDof(section, point, &dof));
7092         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7093       }
7094       break;
7095     case INSERT_BC_VALUES:
7096       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7097         const PetscInt     point = points[2 * p];
7098         const PetscInt    *perm  = perms ? perms[p] : NULL;
7099         const PetscScalar *flip  = flips ? flips[p] : NULL;
7100         PetscCall(PetscSectionGetDof(section, point, &dof));
7101         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7102       }
7103       break;
7104     case ADD_VALUES:
7105       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7106         const PetscInt     point = points[2 * p];
7107         const PetscInt    *perm  = perms ? perms[p] : NULL;
7108         const PetscScalar *flip  = flips ? flips[p] : NULL;
7109         PetscCall(PetscSectionGetDof(section, point, &dof));
7110         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7111       }
7112       break;
7113     case ADD_ALL_VALUES:
7114       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7115         const PetscInt     point = points[2 * p];
7116         const PetscInt    *perm  = perms ? perms[p] : NULL;
7117         const PetscScalar *flip  = flips ? flips[p] : NULL;
7118         PetscCall(PetscSectionGetDof(section, point, &dof));
7119         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7120       }
7121       break;
7122     case ADD_BC_VALUES:
7123       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7124         const PetscInt     point = points[2 * p];
7125         const PetscInt    *perm  = perms ? perms[p] : NULL;
7126         const PetscScalar *flip  = flips ? flips[p] : NULL;
7127         PetscCall(PetscSectionGetDof(section, point, &dof));
7128         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7129       }
7130       break;
7131     default:
7132       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7133     }
7134     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7135   }
7136   /* Cleanup points */
7137   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7138   /* Cleanup array */
7139   PetscCall(VecRestoreArray(v, &array));
7140   PetscFunctionReturn(PETSC_SUCCESS);
7141 }
7142 
7143 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7144 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7145 {
7146   PetscFunctionBegin;
7147   *contains = PETSC_TRUE;
7148   if (label) {
7149     PetscInt fdof;
7150 
7151     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7152     if (!*contains) {
7153       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7154       *offset += fdof;
7155       PetscFunctionReturn(PETSC_SUCCESS);
7156     }
7157   }
7158   PetscFunctionReturn(PETSC_SUCCESS);
7159 }
7160 
7161 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7162 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)
7163 {
7164   PetscSection    clSection;
7165   IS              clPoints;
7166   PetscScalar    *array;
7167   PetscInt       *points = NULL;
7168   const PetscInt *clp;
7169   PetscInt        numFields, numPoints, p;
7170   PetscInt        offset = 0, f;
7171 
7172   PetscFunctionBeginHot;
7173   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7174   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7175   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7176   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7177   PetscCall(PetscSectionGetNumFields(section, &numFields));
7178   /* Get points */
7179   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7180   /* Get array */
7181   PetscCall(VecGetArray(v, &array));
7182   /* Get values */
7183   for (f = 0; f < numFields; ++f) {
7184     const PetscInt    **perms = NULL;
7185     const PetscScalar **flips = NULL;
7186     PetscBool           contains;
7187 
7188     if (!fieldActive[f]) {
7189       for (p = 0; p < numPoints * 2; p += 2) {
7190         PetscInt fdof;
7191         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7192         offset += fdof;
7193       }
7194       continue;
7195     }
7196     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7197     switch (mode) {
7198     case INSERT_VALUES:
7199       for (p = 0; p < numPoints; p++) {
7200         const PetscInt     point = points[2 * p];
7201         const PetscInt    *perm  = perms ? perms[p] : NULL;
7202         const PetscScalar *flip  = flips ? flips[p] : NULL;
7203         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7204         if (!contains) continue;
7205         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7206       }
7207       break;
7208     case INSERT_ALL_VALUES:
7209       for (p = 0; p < numPoints; p++) {
7210         const PetscInt     point = points[2 * p];
7211         const PetscInt    *perm  = perms ? perms[p] : NULL;
7212         const PetscScalar *flip  = flips ? flips[p] : NULL;
7213         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7214         if (!contains) continue;
7215         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7216       }
7217       break;
7218     case INSERT_BC_VALUES:
7219       for (p = 0; p < numPoints; p++) {
7220         const PetscInt     point = points[2 * p];
7221         const PetscInt    *perm  = perms ? perms[p] : NULL;
7222         const PetscScalar *flip  = flips ? flips[p] : NULL;
7223         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7224         if (!contains) continue;
7225         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7226       }
7227       break;
7228     case ADD_VALUES:
7229       for (p = 0; p < numPoints; p++) {
7230         const PetscInt     point = points[2 * p];
7231         const PetscInt    *perm  = perms ? perms[p] : NULL;
7232         const PetscScalar *flip  = flips ? flips[p] : NULL;
7233         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7234         if (!contains) continue;
7235         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7236       }
7237       break;
7238     case ADD_ALL_VALUES:
7239       for (p = 0; p < numPoints; p++) {
7240         const PetscInt     point = points[2 * p];
7241         const PetscInt    *perm  = perms ? perms[p] : NULL;
7242         const PetscScalar *flip  = flips ? flips[p] : NULL;
7243         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7244         if (!contains) continue;
7245         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7246       }
7247       break;
7248     default:
7249       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7250     }
7251     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7252   }
7253   /* Cleanup points */
7254   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7255   /* Cleanup array */
7256   PetscCall(VecRestoreArray(v, &array));
7257   PetscFunctionReturn(PETSC_SUCCESS);
7258 }
7259 
7260 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7261 {
7262   PetscMPIInt rank;
7263   PetscInt    i, j;
7264 
7265   PetscFunctionBegin;
7266   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7267   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7268   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7269   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7270   numCIndices = numCIndices ? numCIndices : numRIndices;
7271   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7272   for (i = 0; i < numRIndices; i++) {
7273     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7274     for (j = 0; j < numCIndices; j++) {
7275 #if defined(PETSC_USE_COMPLEX)
7276       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7277 #else
7278       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7279 #endif
7280     }
7281     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7282   }
7283   PetscFunctionReturn(PETSC_SUCCESS);
7284 }
7285 
7286 /*
7287   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7288 
7289   Input Parameters:
7290 + section - The section for this data layout
7291 . islocal - Is the section (and thus indices being requested) local or global?
7292 . point   - The point contributing dofs with these indices
7293 . off     - The global offset of this point
7294 . loff    - The local offset of each field
7295 . setBC   - The flag determining whether to include indices of boundary values
7296 . perm    - A permutation of the dofs on this point, or NULL
7297 - indperm - A permutation of the entire indices array, or NULL
7298 
7299   Output Parameter:
7300 . indices - Indices for dofs on this point
7301 
7302   Level: developer
7303 
7304   Note: The indices could be local or global, depending on the value of 'off'.
7305 */
7306 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7307 {
7308   PetscInt        dof;   /* The number of unknowns on this point */
7309   PetscInt        cdof;  /* The number of constraints on this point */
7310   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7311   PetscInt        cind = 0, k;
7312 
7313   PetscFunctionBegin;
7314   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7315   PetscCall(PetscSectionGetDof(section, point, &dof));
7316   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7317   if (!cdof || setBC) {
7318     for (k = 0; k < dof; ++k) {
7319       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7320       const PetscInt ind    = indperm ? indperm[preind] : preind;
7321 
7322       indices[ind] = off + k;
7323     }
7324   } else {
7325     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7326     for (k = 0; k < dof; ++k) {
7327       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7328       const PetscInt ind    = indperm ? indperm[preind] : preind;
7329 
7330       if ((cind < cdof) && (k == cdofs[cind])) {
7331         /* Insert check for returning constrained indices */
7332         indices[ind] = -(off + k + 1);
7333         ++cind;
7334       } else {
7335         indices[ind] = off + k - (islocal ? 0 : cind);
7336       }
7337     }
7338   }
7339   *loff += dof;
7340   PetscFunctionReturn(PETSC_SUCCESS);
7341 }
7342 
7343 /*
7344  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7345 
7346  Input Parameters:
7347 + section - a section (global or local)
7348 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7349 . point - point within section
7350 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7351 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7352 . setBC - identify constrained (boundary condition) points via involution.
7353 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7354 . permsoff - offset
7355 - indperm - index permutation
7356 
7357  Output Parameter:
7358 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7359 . indices - array to hold indices (as defined by section) of each dof associated with point
7360 
7361  Notes:
7362  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7363  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7364  in the local vector.
7365 
7366  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7367  significant).  It is invalid to call with a global section and setBC=true.
7368 
7369  Developer Note:
7370  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7371  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7372  offset could be obtained from the section instead of passing it explicitly as we do now.
7373 
7374  Example:
7375  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7376  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7377  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7378  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.
7379 
7380  Level: developer
7381 */
7382 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[])
7383 {
7384   PetscInt numFields, foff, f;
7385 
7386   PetscFunctionBegin;
7387   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7388   PetscCall(PetscSectionGetNumFields(section, &numFields));
7389   for (f = 0, foff = 0; f < numFields; ++f) {
7390     PetscInt        fdof, cfdof;
7391     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7392     PetscInt        cind = 0, b;
7393     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7394 
7395     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7396     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7397     if (!cfdof || setBC) {
7398       for (b = 0; b < fdof; ++b) {
7399         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7400         const PetscInt ind    = indperm ? indperm[preind] : preind;
7401 
7402         indices[ind] = off + foff + b;
7403       }
7404     } else {
7405       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7406       for (b = 0; b < fdof; ++b) {
7407         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7408         const PetscInt ind    = indperm ? indperm[preind] : preind;
7409 
7410         if ((cind < cfdof) && (b == fcdofs[cind])) {
7411           indices[ind] = -(off + foff + b + 1);
7412           ++cind;
7413         } else {
7414           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7415         }
7416       }
7417     }
7418     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7419     foffs[f] += fdof;
7420   }
7421   PetscFunctionReturn(PETSC_SUCCESS);
7422 }
7423 
7424 /*
7425   This version believes the globalSection offsets for each field, rather than just the point offset
7426 
7427  . foffs - The offset into 'indices' for each field, since it is segregated by field
7428 
7429  Notes:
7430  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7431  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7432 */
7433 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7434 {
7435   PetscInt numFields, foff, f;
7436 
7437   PetscFunctionBegin;
7438   PetscCall(PetscSectionGetNumFields(section, &numFields));
7439   for (f = 0; f < numFields; ++f) {
7440     PetscInt        fdof, cfdof;
7441     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7442     PetscInt        cind = 0, b;
7443     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7444 
7445     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7446     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7447     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7448     if (!cfdof) {
7449       for (b = 0; b < fdof; ++b) {
7450         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7451         const PetscInt ind    = indperm ? indperm[preind] : preind;
7452 
7453         indices[ind] = foff + b;
7454       }
7455     } else {
7456       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7457       for (b = 0; b < fdof; ++b) {
7458         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7459         const PetscInt ind    = indperm ? indperm[preind] : preind;
7460 
7461         if ((cind < cfdof) && (b == fcdofs[cind])) {
7462           indices[ind] = -(foff + b + 1);
7463           ++cind;
7464         } else {
7465           indices[ind] = foff + b - cind;
7466         }
7467       }
7468     }
7469     foffs[f] += fdof;
7470   }
7471   PetscFunctionReturn(PETSC_SUCCESS);
7472 }
7473 
7474 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7475 {
7476   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7477 
7478   PetscFunctionBegin;
7479   PetscCall(PetscSectionGetNumFields(section, &numFields));
7480   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7481   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7482   for (PetscInt p = 0; p < nPoints; p++) {
7483     PetscInt     b       = pnts[2 * p];
7484     PetscInt     bSecDof = 0, bOff;
7485     PetscInt     cSecDof = 0;
7486     PetscSection indices_section;
7487 
7488     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7489     if (!bSecDof) continue;
7490     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7491     indices_section = cSecDof > 0 ? cSec : section;
7492     if (numFields) {
7493       PetscInt fStart[32], fEnd[32];
7494 
7495       fStart[0] = 0;
7496       fEnd[0]   = 0;
7497       for (PetscInt f = 0; f < numFields; f++) {
7498         PetscInt fDof = 0;
7499 
7500         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7501         fStart[f + 1] = fStart[f] + fDof;
7502         fEnd[f + 1]   = fStart[f + 1];
7503       }
7504       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7505       // only apply permutations on one side
7506       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7507       for (PetscInt f = 0; f < numFields; f++) {
7508         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7509       }
7510     } else {
7511       PetscInt bEnd = 0;
7512 
7513       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7514       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7515 
7516       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7517     }
7518   }
7519   PetscFunctionReturn(PETSC_SUCCESS);
7520 }
7521 
7522 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[])
7523 {
7524   Mat             cMat;
7525   PetscSection    aSec, cSec;
7526   IS              aIS;
7527   PetscInt        aStart = -1, aEnd = -1;
7528   PetscInt        sStart = -1, sEnd = -1;
7529   PetscInt        cStart = -1, cEnd = -1;
7530   const PetscInt *anchors;
7531   PetscInt        numFields, p;
7532   PetscInt        newNumPoints = 0, newNumIndices = 0;
7533   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7534   PetscInt        oldOffsets[32];
7535   PetscInt        newOffsets[32];
7536   PetscInt        oldOffsetsCopy[32];
7537   PetscInt        newOffsetsCopy[32];
7538   PetscScalar    *modMat         = NULL;
7539   PetscBool       anyConstrained = PETSC_FALSE;
7540 
7541   PetscFunctionBegin;
7542   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7543   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7544   PetscCall(PetscSectionGetNumFields(section, &numFields));
7545 
7546   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7547   /* if there are point-to-point constraints */
7548   if (aSec) {
7549     PetscCall(PetscArrayzero(newOffsets, 32));
7550     PetscCall(PetscArrayzero(oldOffsets, 32));
7551     PetscCall(ISGetIndices(aIS, &anchors));
7552     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7553     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7554     /* figure out how many points are going to be in the new element matrix
7555      * (we allow double counting, because it's all just going to be summed
7556      * into the global matrix anyway) */
7557     for (p = 0; p < 2 * numPoints; p += 2) {
7558       PetscInt b    = points[p];
7559       PetscInt bDof = 0, bSecDof = 0;
7560 
7561       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7562       if (!bSecDof) continue;
7563 
7564       for (PetscInt f = 0; f < numFields; f++) {
7565         PetscInt fDof = 0;
7566 
7567         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7568         oldOffsets[f + 1] += fDof;
7569       }
7570       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7571       if (bDof) {
7572         /* this point is constrained */
7573         /* it is going to be replaced by its anchors */
7574         PetscInt bOff, q;
7575 
7576         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7577         for (q = 0; q < bDof; q++) {
7578           PetscInt a    = anchors[bOff + q];
7579           PetscInt aDof = 0;
7580 
7581           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7582           if (aDof) {
7583             anyConstrained = PETSC_TRUE;
7584             newNumPoints += 1;
7585           }
7586           newNumIndices += aDof;
7587           for (PetscInt f = 0; f < numFields; ++f) {
7588             PetscInt fDof = 0;
7589 
7590             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7591             newOffsets[f + 1] += fDof;
7592           }
7593         }
7594       } else {
7595         /* this point is not constrained */
7596         newNumPoints++;
7597         newNumIndices += bSecDof;
7598         for (PetscInt f = 0; f < numFields; ++f) {
7599           PetscInt fDof;
7600 
7601           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7602           newOffsets[f + 1] += fDof;
7603         }
7604       }
7605     }
7606   }
7607   if (!anyConstrained) {
7608     if (outNumPoints) *outNumPoints = 0;
7609     if (outNumIndices) *outNumIndices = 0;
7610     if (outPoints) *outPoints = NULL;
7611     if (outMat) *outMat = NULL;
7612     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7613     PetscFunctionReturn(PETSC_SUCCESS);
7614   }
7615 
7616   if (outNumPoints) *outNumPoints = newNumPoints;
7617   if (outNumIndices) *outNumIndices = newNumIndices;
7618 
7619   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7620   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7621 
7622   if (!outPoints && !outMat) {
7623     if (offsets) {
7624       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7625     }
7626     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7627     PetscFunctionReturn(PETSC_SUCCESS);
7628   }
7629 
7630   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7631   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7632 
7633   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7634   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7635 
7636   /* output arrays */
7637   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7638   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7639 
7640   // get the new Points
7641   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7642     PetscInt b    = points[2 * p];
7643     PetscInt bDof = 0, bSecDof = 0, bOff;
7644 
7645     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7646     if (!bSecDof) continue;
7647     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7648     if (bDof) {
7649       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7650       for (PetscInt q = 0; q < bDof; q++) {
7651         PetscInt a = anchors[bOff + q], aDof = 0;
7652 
7653         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7654         if (aDof) {
7655           newPoints[2 * newP]     = a;
7656           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7657           newP++;
7658         }
7659       }
7660     } else {
7661       newPoints[2 * newP]     = b;
7662       newPoints[2 * newP + 1] = points[2 * p + 1];
7663       newP++;
7664     }
7665   }
7666 
7667   if (outMat) {
7668     PetscScalar *tmpMat;
7669     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7670     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7671 
7672     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7673     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7674     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7675     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7676 
7677     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7678     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7679 
7680     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7681     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7682 
7683     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7684     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7685     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7686     // for each field, insert the anchor modification into modMat
7687     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7688       PetscInt fStart    = oldOffsets[f];
7689       PetscInt fNewStart = newOffsets[f];
7690       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7691         PetscInt b    = points[2 * p];
7692         PetscInt bDof = 0, bSecDof = 0, bOff;
7693 
7694         if (b >= sStart && b < sEnd) {
7695           if (numFields) {
7696             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7697           } else {
7698             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7699           }
7700         }
7701         if (!bSecDof) continue;
7702         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7703         if (bDof) {
7704           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7705           for (PetscInt q = 0; q < bDof; q++, newP++) {
7706             PetscInt a = anchors[bOff + q], aDof = 0;
7707 
7708             if (a >= sStart && a < sEnd) {
7709               if (numFields) {
7710                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7711               } else {
7712                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7713               }
7714             }
7715             if (aDof) {
7716               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7717               for (PetscInt d = 0; d < bSecDof; d++) {
7718                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7719               }
7720             }
7721             oNew += aDof;
7722           }
7723         } else {
7724           // Insert the identity matrix in this block
7725           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7726           oNew += bSecDof;
7727           newP++;
7728         }
7729         o += bSecDof;
7730       }
7731     }
7732 
7733     *outMat = modMat;
7734 
7735     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7736     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7737     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7738     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7739     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7740   }
7741   PetscCall(ISRestoreIndices(aIS, &anchors));
7742 
7743   /* output */
7744   if (outPoints) {
7745     *outPoints = newPoints;
7746   } else {
7747     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7748   }
7749   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7750   PetscFunctionReturn(PETSC_SUCCESS);
7751 }
7752 
7753 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)
7754 {
7755   PetscScalar *modMat        = NULL;
7756   PetscInt     newNumIndices = -1;
7757 
7758   PetscFunctionBegin;
7759   /* 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.
7760      modMat is that matrix C */
7761   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7762   if (outNumIndices) *outNumIndices = newNumIndices;
7763   if (modMat) {
7764     const PetscScalar *newValues = values;
7765 
7766     if (multiplyRight) {
7767       PetscScalar *newNewValues = NULL;
7768       PetscBLASInt M            = newNumIndices;
7769       PetscBLASInt N            = numRows;
7770       PetscBLASInt K            = numIndices;
7771       PetscScalar  a = 1.0, b = 0.0;
7772 
7773       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);
7774 
7775       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7776       // row-major to column-major conversion, right multiplication becomes left multiplication
7777       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7778 
7779       numCols   = newNumIndices;
7780       newValues = newNewValues;
7781     }
7782 
7783     if (multiplyLeft) {
7784       PetscScalar *newNewValues = NULL;
7785       PetscBLASInt M            = numCols;
7786       PetscBLASInt N            = newNumIndices;
7787       PetscBLASInt K            = numIndices;
7788       PetscScalar  a = 1.0, b = 0.0;
7789 
7790       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);
7791 
7792       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7793       // row-major to column-major conversion, left multiplication becomes right multiplication
7794       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7795       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7796       newValues = newNewValues;
7797     }
7798     *outValues = (PetscScalar *)newValues;
7799     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7800   }
7801   PetscFunctionReturn(PETSC_SUCCESS);
7802 }
7803 
7804 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)
7805 {
7806   PetscFunctionBegin;
7807   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7808   PetscFunctionReturn(PETSC_SUCCESS);
7809 }
7810 
7811 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7812 {
7813   /* Closure ordering */
7814   PetscSection    clSection;
7815   IS              clPoints;
7816   const PetscInt *clp;
7817   PetscInt       *points;
7818   PetscInt        Ncl, Ni = 0;
7819 
7820   PetscFunctionBeginHot;
7821   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7822   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7823     PetscInt dof;
7824 
7825     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7826     Ni += dof;
7827   }
7828   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7829   *closureSize = Ni;
7830   PetscFunctionReturn(PETSC_SUCCESS);
7831 }
7832 
7833 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)
7834 {
7835   /* Closure ordering */
7836   PetscSection    clSection;
7837   IS              clPoints;
7838   const PetscInt *clp;
7839   PetscInt       *points;
7840   const PetscInt *clperm = NULL;
7841   /* Dof permutation and sign flips */
7842   const PetscInt    **perms[32] = {NULL};
7843   const PetscScalar **flips[32] = {NULL};
7844   PetscScalar        *valCopy   = NULL;
7845   /* Hanging node constraints */
7846   PetscInt    *pointsC = NULL;
7847   PetscScalar *valuesC = NULL;
7848   PetscInt     NclC, NiC;
7849 
7850   PetscInt *idx;
7851   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7852   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7853   PetscInt  idxStart, idxEnd;
7854   PetscInt  nRows, nCols;
7855 
7856   PetscFunctionBeginHot;
7857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7858   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7859   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7860   PetscAssertPointer(numRows, 6);
7861   PetscAssertPointer(numCols, 7);
7862   if (indices) PetscAssertPointer(indices, 8);
7863   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7864   if (values) PetscAssertPointer(values, 10);
7865   PetscCall(PetscSectionGetNumFields(section, &Nf));
7866   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7867   PetscCall(PetscArrayzero(offsets, 32));
7868   /* 1) Get points in closure */
7869   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7870   if (useClPerm) {
7871     PetscInt depth, clsize;
7872     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7873     for (clsize = 0, p = 0; p < Ncl; p++) {
7874       PetscInt dof;
7875       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7876       clsize += dof;
7877     }
7878     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7879   }
7880   /* 2) Get number of indices on these points and field offsets from section */
7881   for (p = 0; p < Ncl * 2; p += 2) {
7882     PetscInt dof, fdof;
7883 
7884     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7885     for (f = 0; f < Nf; ++f) {
7886       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7887       offsets[f + 1] += fdof;
7888     }
7889     Ni += dof;
7890   }
7891   if (*numRows == -1) *numRows = Ni;
7892   if (*numCols == -1) *numCols = Ni;
7893   nRows = *numRows;
7894   nCols = *numCols;
7895   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7896   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7897   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7898   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7899   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7900   for (f = 0; f < PetscMax(1, Nf); ++f) {
7901     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7902     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7903     /* may need to apply sign changes to the element matrix */
7904     if (values && flips[f]) {
7905       PetscInt foffset = offsets[f];
7906 
7907       for (p = 0; p < Ncl; ++p) {
7908         PetscInt           pnt  = points[2 * p], fdof;
7909         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7910 
7911         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7912         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7913         if (flip) {
7914           PetscInt i, j, k;
7915 
7916           if (!valCopy) {
7917             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7918             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7919             *values = valCopy;
7920           }
7921           for (i = 0; i < fdof; ++i) {
7922             PetscScalar fval = flip[i];
7923 
7924             if (multiplyRight) {
7925               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
7926             }
7927             if (multiplyLeft) {
7928               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
7929             }
7930           }
7931         }
7932         foffset += fdof;
7933       }
7934     }
7935   }
7936   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7937   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
7938   if (NclC) {
7939     if (multiplyRight) { *numCols = nCols = NiC; }
7940     if (multiplyLeft) { *numRows = nRows = NiC; }
7941     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7942     for (f = 0; f < PetscMax(1, Nf); ++f) {
7943       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7944       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7945     }
7946     for (f = 0; f < PetscMax(1, Nf); ++f) {
7947       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7948       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7949     }
7950     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7951     Ncl    = NclC;
7952     Ni     = NiC;
7953     points = pointsC;
7954     if (values) *values = valuesC;
7955   }
7956   /* 5) Calculate indices */
7957   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7958   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
7959   if (Nf) {
7960     PetscInt  idxOff;
7961     PetscBool useFieldOffsets;
7962 
7963     if (outOffsets) {
7964       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7965     }
7966     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7967     if (useFieldOffsets) {
7968       for (p = 0; p < Ncl; ++p) {
7969         const PetscInt pnt = points[p * 2];
7970 
7971         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7972       }
7973     } else {
7974       for (p = 0; p < Ncl; ++p) {
7975         const PetscInt pnt = points[p * 2];
7976 
7977         if (pnt < idxStart || pnt >= idxEnd) continue;
7978         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7979         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7980          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7981          * global section. */
7982         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7983       }
7984     }
7985   } else {
7986     PetscInt off = 0, idxOff;
7987 
7988     for (p = 0; p < Ncl; ++p) {
7989       const PetscInt  pnt  = points[p * 2];
7990       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7991 
7992       if (pnt < idxStart || pnt >= idxEnd) continue;
7993       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7994       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7995        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7996       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7997     }
7998   }
7999   /* 6) Cleanup */
8000   for (f = 0; f < PetscMax(1, Nf); ++f) {
8001     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8002     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8003   }
8004   if (NclC) {
8005     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8006   } else {
8007     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8008   }
8009 
8010   if (indices) *indices = idx;
8011   PetscFunctionReturn(PETSC_SUCCESS);
8012 }
8013 
8014 /*@C
8015   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8016 
8017   Not collective
8018 
8019   Input Parameters:
8020 + dm         - The `DM`
8021 . section    - The `PetscSection` describing the points (a local section)
8022 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8023 . point      - The point defining the closure
8024 - useClPerm  - Use the closure point permutation if available
8025 
8026   Output Parameters:
8027 + numIndices - The number of dof indices in the closure of point with the input sections
8028 . indices    - The dof indices
8029 . outOffsets - Array to write the field offsets into, or `NULL`
8030 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8031 
8032   Level: advanced
8033 
8034   Notes:
8035   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
8036 
8037   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8038   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8039   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8040   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8041   indices (with the above semantics) are implied.
8042 
8043 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8044           `PetscSection`, `DMGetGlobalSection()`
8045 @*/
8046 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8047 {
8048   PetscInt numRows = -1, numCols = -1;
8049 
8050   PetscFunctionBeginHot;
8051   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8052   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8053   *numIndices = numRows;
8054   PetscFunctionReturn(PETSC_SUCCESS);
8055 }
8056 
8057 /*@C
8058   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8059 
8060   Not collective
8061 
8062   Input Parameters:
8063 + dm         - The `DM`
8064 . section    - The `PetscSection` describing the points (a local section)
8065 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8066 . point      - The point defining the closure
8067 - useClPerm  - Use the closure point permutation if available
8068 
8069   Output Parameters:
8070 + numIndices - The number of dof indices in the closure of point with the input sections
8071 . indices    - The dof indices
8072 . outOffsets - Array to write the field offsets into, or `NULL`
8073 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8074 
8075   Level: advanced
8076 
8077   Notes:
8078   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8079 
8080   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8081   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8082   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8083   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8084   indices (with the above semantics) are implied.
8085 
8086 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8087 @*/
8088 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8089 {
8090   PetscFunctionBegin;
8091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8092   PetscAssertPointer(indices, 7);
8093   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8094   PetscFunctionReturn(PETSC_SUCCESS);
8095 }
8096 
8097 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8098 {
8099   DM_Plex           *mesh = (DM_Plex *)dm->data;
8100   PetscInt          *indices;
8101   PetscInt           numIndices;
8102   const PetscScalar *valuesOrig = values;
8103   PetscErrorCode     ierr;
8104 
8105   PetscFunctionBegin;
8106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8107   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8108   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8109   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8110   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8111   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8112 
8113   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8114 
8115   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8116   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8117   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8118   if (ierr) {
8119     PetscMPIInt rank;
8120 
8121     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8122     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8123     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8124     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8125     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8126     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8127   }
8128   if (mesh->printFEM > 1) {
8129     PetscInt i;
8130     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8131     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8132     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8133   }
8134 
8135   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8136   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8137   PetscFunctionReturn(PETSC_SUCCESS);
8138 }
8139 
8140 /*@C
8141   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8142 
8143   Not collective
8144 
8145   Input Parameters:
8146 + dm            - The `DM`
8147 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8148 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8149 . A             - The matrix
8150 . point         - The point in the `DM`
8151 . values        - The array of values
8152 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8153 
8154   Level: intermediate
8155 
8156 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8157 @*/
8158 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8159 {
8160   PetscFunctionBegin;
8161   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8162   PetscFunctionReturn(PETSC_SUCCESS);
8163 }
8164 
8165 /*@C
8166   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8167 
8168   Not collective
8169 
8170   Input Parameters:
8171 + dmRow            - The `DM` for the row fields
8172 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8173 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8174 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8175 . dmCol            - The `DM` for the column fields
8176 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8177 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8178 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8179 . A                - The matrix
8180 . point            - The point in the `DM`
8181 . values           - The array of values
8182 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8183 
8184   Level: intermediate
8185 
8186 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8187 @*/
8188 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)
8189 {
8190   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8191   PetscInt          *indicesRow, *indicesCol;
8192   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8193   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8194 
8195   PetscErrorCode ierr;
8196 
8197   PetscFunctionBegin;
8198   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8199   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8200   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8201   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8202   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8203   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8204   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8205   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8206   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8207   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8208   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8209 
8210   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8211   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8212   valuesV1 = valuesV0;
8213   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8214   valuesV2 = valuesV1;
8215   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8216 
8217   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8218   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8219   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8220   if (ierr) {
8221     PetscMPIInt rank;
8222 
8223     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8224     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8225     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8226     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8227     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8228     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8229     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8230   }
8231 
8232   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8233   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8234   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8235   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8236   PetscFunctionReturn(PETSC_SUCCESS);
8237 }
8238 
8239 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8240 {
8241   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8242   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8243   PetscInt       *cpoints = NULL;
8244   PetscInt       *findices, *cindices;
8245   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8246   PetscInt        foffsets[32], coffsets[32];
8247   DMPolytopeType  ct;
8248   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8249   PetscErrorCode  ierr;
8250 
8251   PetscFunctionBegin;
8252   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8253   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8254   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8255   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8256   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8257   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8258   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8259   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8260   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8261   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8262   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8263   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8264   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8265   PetscCall(PetscArrayzero(foffsets, 32));
8266   PetscCall(PetscArrayzero(coffsets, 32));
8267   /* Column indices */
8268   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8269   maxFPoints = numCPoints;
8270   /* Compress out points not in the section */
8271   /*   TODO: Squeeze out points with 0 dof as well */
8272   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8273   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8274     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8275       cpoints[q * 2]     = cpoints[p];
8276       cpoints[q * 2 + 1] = cpoints[p + 1];
8277       ++q;
8278     }
8279   }
8280   numCPoints = q;
8281   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8282     PetscInt fdof;
8283 
8284     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8285     if (!dof) continue;
8286     for (f = 0; f < numFields; ++f) {
8287       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8288       coffsets[f + 1] += fdof;
8289     }
8290     numCIndices += dof;
8291   }
8292   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8293   /* Row indices */
8294   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8295   {
8296     DMPlexTransform tr;
8297     DMPolytopeType *rct;
8298     PetscInt       *rsize, *rcone, *rornt, Nt;
8299 
8300     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8301     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8302     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8303     numSubcells = rsize[Nt - 1];
8304     PetscCall(DMPlexTransformDestroy(&tr));
8305   }
8306   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8307   for (r = 0, q = 0; r < numSubcells; ++r) {
8308     /* TODO Map from coarse to fine cells */
8309     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8310     /* Compress out points not in the section */
8311     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8312     for (p = 0; p < numFPoints * 2; p += 2) {
8313       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8314         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8315         if (!dof) continue;
8316         for (s = 0; s < q; ++s)
8317           if (fpoints[p] == ftotpoints[s * 2]) break;
8318         if (s < q) continue;
8319         ftotpoints[q * 2]     = fpoints[p];
8320         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8321         ++q;
8322       }
8323     }
8324     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8325   }
8326   numFPoints = q;
8327   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8328     PetscInt fdof;
8329 
8330     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8331     if (!dof) continue;
8332     for (f = 0; f < numFields; ++f) {
8333       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8334       foffsets[f + 1] += fdof;
8335     }
8336     numFIndices += dof;
8337   }
8338   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8339 
8340   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8341   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8342   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8343   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8344   if (numFields) {
8345     const PetscInt **permsF[32] = {NULL};
8346     const PetscInt **permsC[32] = {NULL};
8347 
8348     for (f = 0; f < numFields; f++) {
8349       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8350       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8351     }
8352     for (p = 0; p < numFPoints; p++) {
8353       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8354       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8355     }
8356     for (p = 0; p < numCPoints; p++) {
8357       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8358       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8359     }
8360     for (f = 0; f < numFields; f++) {
8361       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8362       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8363     }
8364   } else {
8365     const PetscInt **permsF = NULL;
8366     const PetscInt **permsC = NULL;
8367 
8368     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8369     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8370     for (p = 0, off = 0; p < numFPoints; p++) {
8371       const PetscInt *perm = permsF ? permsF[p] : NULL;
8372 
8373       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8374       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8375     }
8376     for (p = 0, off = 0; p < numCPoints; p++) {
8377       const PetscInt *perm = permsC ? permsC[p] : NULL;
8378 
8379       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8380       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8381     }
8382     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8383     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8384   }
8385   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8386   /* TODO: flips */
8387   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8388   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8389   if (ierr) {
8390     PetscMPIInt rank;
8391 
8392     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8393     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8394     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8395     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8396     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8397   }
8398   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8399   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8400   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8401   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8402   PetscFunctionReturn(PETSC_SUCCESS);
8403 }
8404 
8405 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8406 {
8407   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8408   PetscInt       *cpoints      = NULL;
8409   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8410   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8411   DMPolytopeType  ct;
8412   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8413 
8414   PetscFunctionBegin;
8415   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8416   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8417   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8418   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8419   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8420   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8421   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8422   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8423   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8424   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8425   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8426   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8427   /* Column indices */
8428   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8429   maxFPoints = numCPoints;
8430   /* Compress out points not in the section */
8431   /*   TODO: Squeeze out points with 0 dof as well */
8432   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8433   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8434     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8435       cpoints[q * 2]     = cpoints[p];
8436       cpoints[q * 2 + 1] = cpoints[p + 1];
8437       ++q;
8438     }
8439   }
8440   numCPoints = q;
8441   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8442     PetscInt fdof;
8443 
8444     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8445     if (!dof) continue;
8446     for (f = 0; f < numFields; ++f) {
8447       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8448       coffsets[f + 1] += fdof;
8449     }
8450     numCIndices += dof;
8451   }
8452   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8453   /* Row indices */
8454   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8455   {
8456     DMPlexTransform tr;
8457     DMPolytopeType *rct;
8458     PetscInt       *rsize, *rcone, *rornt, Nt;
8459 
8460     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8461     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8462     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8463     numSubcells = rsize[Nt - 1];
8464     PetscCall(DMPlexTransformDestroy(&tr));
8465   }
8466   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8467   for (r = 0, q = 0; r < numSubcells; ++r) {
8468     /* TODO Map from coarse to fine cells */
8469     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8470     /* Compress out points not in the section */
8471     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8472     for (p = 0; p < numFPoints * 2; p += 2) {
8473       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8474         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8475         if (!dof) continue;
8476         for (s = 0; s < q; ++s)
8477           if (fpoints[p] == ftotpoints[s * 2]) break;
8478         if (s < q) continue;
8479         ftotpoints[q * 2]     = fpoints[p];
8480         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8481         ++q;
8482       }
8483     }
8484     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8485   }
8486   numFPoints = q;
8487   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8488     PetscInt fdof;
8489 
8490     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8491     if (!dof) continue;
8492     for (f = 0; f < numFields; ++f) {
8493       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8494       foffsets[f + 1] += fdof;
8495     }
8496     numFIndices += dof;
8497   }
8498   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8499 
8500   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8501   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8502   if (numFields) {
8503     const PetscInt **permsF[32] = {NULL};
8504     const PetscInt **permsC[32] = {NULL};
8505 
8506     for (f = 0; f < numFields; f++) {
8507       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8508       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8509     }
8510     for (p = 0; p < numFPoints; p++) {
8511       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8512       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8513     }
8514     for (p = 0; p < numCPoints; p++) {
8515       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8516       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8517     }
8518     for (f = 0; f < numFields; f++) {
8519       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8520       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8521     }
8522   } else {
8523     const PetscInt **permsF = NULL;
8524     const PetscInt **permsC = NULL;
8525 
8526     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8527     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8528     for (p = 0, off = 0; p < numFPoints; p++) {
8529       const PetscInt *perm = permsF ? permsF[p] : NULL;
8530 
8531       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8532       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8533     }
8534     for (p = 0, off = 0; p < numCPoints; p++) {
8535       const PetscInt *perm = permsC ? permsC[p] : NULL;
8536 
8537       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8538       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8539     }
8540     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8541     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8542   }
8543   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8544   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8545   PetscFunctionReturn(PETSC_SUCCESS);
8546 }
8547 
8548 /*@
8549   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8550 
8551   Input Parameter:
8552 . dm - The `DMPLEX` object
8553 
8554   Output Parameter:
8555 . cellHeight - The height of a cell
8556 
8557   Level: developer
8558 
8559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8560 @*/
8561 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8562 {
8563   DM_Plex *mesh = (DM_Plex *)dm->data;
8564 
8565   PetscFunctionBegin;
8566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8567   PetscAssertPointer(cellHeight, 2);
8568   *cellHeight = mesh->vtkCellHeight;
8569   PetscFunctionReturn(PETSC_SUCCESS);
8570 }
8571 
8572 /*@
8573   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8574 
8575   Input Parameters:
8576 + dm         - The `DMPLEX` object
8577 - cellHeight - The height of a cell
8578 
8579   Level: developer
8580 
8581 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8582 @*/
8583 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8584 {
8585   DM_Plex *mesh = (DM_Plex *)dm->data;
8586 
8587   PetscFunctionBegin;
8588   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8589   mesh->vtkCellHeight = cellHeight;
8590   PetscFunctionReturn(PETSC_SUCCESS);
8591 }
8592 
8593 /*@
8594   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8595 
8596   Input Parameters:
8597 + dm - The `DMPLEX` object
8598 - ct - The `DMPolytopeType` of the cell
8599 
8600   Output Parameters:
8601 + start - The first cell of this type, or `NULL`
8602 - end   - The upper bound on this celltype, or `NULL`
8603 
8604   Level: advanced
8605 
8606 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8607 @*/
8608 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8609 {
8610   DM_Plex *mesh = (DM_Plex *)dm->data;
8611   DMLabel  label;
8612   PetscInt pStart, pEnd;
8613 
8614   PetscFunctionBegin;
8615   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8616   if (start) {
8617     PetscAssertPointer(start, 3);
8618     *start = 0;
8619   }
8620   if (end) {
8621     PetscAssertPointer(end, 4);
8622     *end = 0;
8623   }
8624   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8625   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8626   if (mesh->tr) {
8627     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8628   } else {
8629     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8630     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8631     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8632   }
8633   PetscFunctionReturn(PETSC_SUCCESS);
8634 }
8635 
8636 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8637 {
8638   PetscSection section, globalSection;
8639   PetscInt    *numbers, p;
8640 
8641   PetscFunctionBegin;
8642   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8643   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8644   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8645   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8646   PetscCall(PetscSectionSetUp(section));
8647   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8648   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8649   for (p = pStart; p < pEnd; ++p) {
8650     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8651     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8652     else numbers[p - pStart] += shift;
8653   }
8654   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8655   if (globalSize) {
8656     PetscLayout layout;
8657     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8658     PetscCall(PetscLayoutGetSize(layout, globalSize));
8659     PetscCall(PetscLayoutDestroy(&layout));
8660   }
8661   PetscCall(PetscSectionDestroy(&section));
8662   PetscCall(PetscSectionDestroy(&globalSection));
8663   PetscFunctionReturn(PETSC_SUCCESS);
8664 }
8665 
8666 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8667 {
8668   PetscInt cellHeight, cStart, cEnd;
8669 
8670   PetscFunctionBegin;
8671   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8672   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8673   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8674   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8675   PetscFunctionReturn(PETSC_SUCCESS);
8676 }
8677 
8678 /*@
8679   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8680 
8681   Input Parameter:
8682 . dm - The `DMPLEX` object
8683 
8684   Output Parameter:
8685 . globalCellNumbers - Global cell numbers for all cells on this process
8686 
8687   Level: developer
8688 
8689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8690 @*/
8691 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8692 {
8693   DM_Plex *mesh = (DM_Plex *)dm->data;
8694 
8695   PetscFunctionBegin;
8696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8697   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8698   *globalCellNumbers = mesh->globalCellNumbers;
8699   PetscFunctionReturn(PETSC_SUCCESS);
8700 }
8701 
8702 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8703 {
8704   PetscInt vStart, vEnd;
8705 
8706   PetscFunctionBegin;
8707   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8708   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8709   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8710   PetscFunctionReturn(PETSC_SUCCESS);
8711 }
8712 
8713 /*@
8714   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8715 
8716   Input Parameter:
8717 . dm - The `DMPLEX` object
8718 
8719   Output Parameter:
8720 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8721 
8722   Level: developer
8723 
8724 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8725 @*/
8726 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8727 {
8728   DM_Plex *mesh = (DM_Plex *)dm->data;
8729 
8730   PetscFunctionBegin;
8731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8732   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8733   *globalVertexNumbers = mesh->globalVertexNumbers;
8734   PetscFunctionReturn(PETSC_SUCCESS);
8735 }
8736 
8737 /*@
8738   DMPlexCreatePointNumbering - Create a global numbering for all points.
8739 
8740   Collective
8741 
8742   Input Parameter:
8743 . dm - The `DMPLEX` object
8744 
8745   Output Parameter:
8746 . globalPointNumbers - Global numbers for all points on this process
8747 
8748   Level: developer
8749 
8750   Notes:
8751   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8752   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8753   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8754   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8755 
8756   The partitioned mesh is
8757   ```
8758   (2)--0--(3)--1--(4)    (1)--0--(2)
8759   ```
8760   and its global numbering is
8761   ```
8762   (3)--0--(4)--1--(5)--2--(6)
8763   ```
8764   Then the global numbering is provided as
8765   ```
8766   [0] Number of indices in set 5
8767   [0] 0 0
8768   [0] 1 1
8769   [0] 2 3
8770   [0] 3 4
8771   [0] 4 -6
8772   [1] Number of indices in set 3
8773   [1] 0 2
8774   [1] 1 5
8775   [1] 2 6
8776   ```
8777 
8778 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8779 @*/
8780 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8781 {
8782   IS        nums[4];
8783   PetscInt  depths[4], gdepths[4], starts[4];
8784   PetscInt  depth, d, shift = 0;
8785   PetscBool empty = PETSC_FALSE;
8786 
8787   PetscFunctionBegin;
8788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8789   PetscCall(DMPlexGetDepth(dm, &depth));
8790   // For unstratified meshes use dim instead of depth
8791   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8792   // If any stratum is empty, we must mark all empty
8793   for (d = 0; d <= depth; ++d) {
8794     PetscInt end;
8795 
8796     depths[d] = depth - d;
8797     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8798     if (!(starts[d] - end)) empty = PETSC_TRUE;
8799   }
8800   if (empty)
8801     for (d = 0; d <= depth; ++d) {
8802       depths[d] = -1;
8803       starts[d] = -1;
8804     }
8805   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8806   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8807   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]);
8808   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8809   for (d = 0; d <= depth; ++d) {
8810     PetscInt pStart, pEnd, gsize;
8811 
8812     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8813     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8814     shift += gsize;
8815   }
8816   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8817   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8818   PetscFunctionReturn(PETSC_SUCCESS);
8819 }
8820 
8821 /*@
8822   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8823 
8824   Input Parameter:
8825 . dm - The `DMPLEX` object
8826 
8827   Output Parameter:
8828 . ranks - The rank field
8829 
8830   Options Database Key:
8831 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8832 
8833   Level: intermediate
8834 
8835 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8836 @*/
8837 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8838 {
8839   DM             rdm;
8840   PetscFE        fe;
8841   PetscScalar   *r;
8842   PetscMPIInt    rank;
8843   DMPolytopeType ct;
8844   PetscInt       dim, cStart, cEnd, c;
8845   PetscBool      simplex;
8846 
8847   PetscFunctionBeginUser;
8848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8849   PetscAssertPointer(ranks, 2);
8850   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8851   PetscCall(DMClone(dm, &rdm));
8852   PetscCall(DMGetDimension(rdm, &dim));
8853   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8854   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8855   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8856   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8857   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8858   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8859   PetscCall(PetscFEDestroy(&fe));
8860   PetscCall(DMCreateDS(rdm));
8861   PetscCall(DMCreateGlobalVector(rdm, ranks));
8862   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8863   PetscCall(VecGetArray(*ranks, &r));
8864   for (c = cStart; c < cEnd; ++c) {
8865     PetscScalar *lr;
8866 
8867     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8868     if (lr) *lr = rank;
8869   }
8870   PetscCall(VecRestoreArray(*ranks, &r));
8871   PetscCall(DMDestroy(&rdm));
8872   PetscFunctionReturn(PETSC_SUCCESS);
8873 }
8874 
8875 /*@
8876   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8877 
8878   Input Parameters:
8879 + dm    - The `DMPLEX`
8880 - label - The `DMLabel`
8881 
8882   Output Parameter:
8883 . val - The label value field
8884 
8885   Options Database Key:
8886 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8887 
8888   Level: intermediate
8889 
8890 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8891 @*/
8892 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8893 {
8894   DM             rdm, plex;
8895   Vec            lval;
8896   PetscSection   section;
8897   PetscFE        fe;
8898   PetscScalar   *v;
8899   PetscInt       dim, pStart, pEnd, p, cStart;
8900   DMPolytopeType ct;
8901   char           name[PETSC_MAX_PATH_LEN];
8902   const char    *lname, *prefix;
8903 
8904   PetscFunctionBeginUser;
8905   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8906   PetscAssertPointer(label, 2);
8907   PetscAssertPointer(val, 3);
8908   PetscCall(DMClone(dm, &rdm));
8909   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8910   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8911   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8912   PetscCall(DMDestroy(&plex));
8913   PetscCall(DMGetDimension(rdm, &dim));
8914   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8915   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8916   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8917   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8918   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8919   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8920   PetscCall(PetscFEDestroy(&fe));
8921   PetscCall(DMCreateDS(rdm));
8922   PetscCall(DMCreateGlobalVector(rdm, val));
8923   PetscCall(DMCreateLocalVector(rdm, &lval));
8924   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8925   PetscCall(DMGetLocalSection(rdm, &section));
8926   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8927   PetscCall(VecGetArray(lval, &v));
8928   for (p = pStart; p < pEnd; ++p) {
8929     PetscInt cval, dof, off;
8930 
8931     PetscCall(PetscSectionGetDof(section, p, &dof));
8932     if (!dof) continue;
8933     PetscCall(DMLabelGetValue(label, p, &cval));
8934     PetscCall(PetscSectionGetOffset(section, p, &off));
8935     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8936   }
8937   PetscCall(VecRestoreArray(lval, &v));
8938   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8939   PetscCall(VecDestroy(&lval));
8940   PetscCall(DMDestroy(&rdm));
8941   PetscFunctionReturn(PETSC_SUCCESS);
8942 }
8943 
8944 /*@
8945   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8946 
8947   Input Parameter:
8948 . dm - The `DMPLEX` object
8949 
8950   Level: developer
8951 
8952   Notes:
8953   This is a useful diagnostic when creating meshes programmatically.
8954 
8955   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8956 
8957 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8958 @*/
8959 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8960 {
8961   PetscSection    coneSection, supportSection;
8962   const PetscInt *cone, *support;
8963   PetscInt        coneSize, c, supportSize, s;
8964   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8965   PetscBool       storagecheck = PETSC_TRUE;
8966 
8967   PetscFunctionBegin;
8968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8969   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8970   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8971   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8972   /* Check that point p is found in the support of its cone points, and vice versa */
8973   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8974   for (p = pStart; p < pEnd; ++p) {
8975     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8976     PetscCall(DMPlexGetCone(dm, p, &cone));
8977     for (c = 0; c < coneSize; ++c) {
8978       PetscBool dup = PETSC_FALSE;
8979       PetscInt  d;
8980       for (d = c - 1; d >= 0; --d) {
8981         if (cone[c] == cone[d]) {
8982           dup = PETSC_TRUE;
8983           break;
8984         }
8985       }
8986       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8987       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8988       for (s = 0; s < supportSize; ++s) {
8989         if (support[s] == p) break;
8990       }
8991       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8992         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8993         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8994         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8995         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8996         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8997         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8998         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]);
8999         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9000       }
9001     }
9002     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9003     if (p != pp) {
9004       storagecheck = PETSC_FALSE;
9005       continue;
9006     }
9007     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9008     PetscCall(DMPlexGetSupport(dm, p, &support));
9009     for (s = 0; s < supportSize; ++s) {
9010       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9011       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9012       for (c = 0; c < coneSize; ++c) {
9013         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9014         if (cone[c] != pp) {
9015           c = 0;
9016           break;
9017         }
9018         if (cone[c] == p) break;
9019       }
9020       if (c >= coneSize) {
9021         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9022         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9023         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9024         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9025         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9026         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9027         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9028       }
9029     }
9030   }
9031   if (storagecheck) {
9032     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9033     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9034     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9035   }
9036   PetscFunctionReturn(PETSC_SUCCESS);
9037 }
9038 
9039 /*
9040   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.
9041 */
9042 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9043 {
9044   DMPolytopeType  cct;
9045   PetscInt        ptpoints[4];
9046   const PetscInt *cone, *ccone, *ptcone;
9047   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9048 
9049   PetscFunctionBegin;
9050   *unsplit = 0;
9051   switch (ct) {
9052   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9053     ptpoints[npt++] = c;
9054     break;
9055   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9056     PetscCall(DMPlexGetCone(dm, c, &cone));
9057     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9058     for (cp = 0; cp < coneSize; ++cp) {
9059       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9060       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9061     }
9062     break;
9063   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9064   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9065     PetscCall(DMPlexGetCone(dm, c, &cone));
9066     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9067     for (cp = 0; cp < coneSize; ++cp) {
9068       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9069       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9070       for (ccp = 0; ccp < cconeSize; ++ccp) {
9071         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9072         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9073           PetscInt p;
9074           for (p = 0; p < npt; ++p)
9075             if (ptpoints[p] == ccone[ccp]) break;
9076           if (p == npt) ptpoints[npt++] = ccone[ccp];
9077         }
9078       }
9079     }
9080     break;
9081   default:
9082     break;
9083   }
9084   for (pt = 0; pt < npt; ++pt) {
9085     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9086     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9087   }
9088   PetscFunctionReturn(PETSC_SUCCESS);
9089 }
9090 
9091 /*@
9092   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9093 
9094   Input Parameters:
9095 + dm         - The `DMPLEX` object
9096 - cellHeight - Normally 0
9097 
9098   Level: developer
9099 
9100   Notes:
9101   This is a useful diagnostic when creating meshes programmatically.
9102   Currently applicable only to homogeneous simplex or tensor meshes.
9103 
9104   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9105 
9106 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9107 @*/
9108 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9109 {
9110   DMPlexInterpolatedFlag interp;
9111   DMPolytopeType         ct;
9112   PetscInt               vStart, vEnd, cStart, cEnd, c;
9113 
9114   PetscFunctionBegin;
9115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9116   PetscCall(DMPlexIsInterpolated(dm, &interp));
9117   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9118   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9119   for (c = cStart; c < cEnd; ++c) {
9120     PetscInt *closure = NULL;
9121     PetscInt  coneSize, closureSize, cl, Nv = 0;
9122 
9123     PetscCall(DMPlexGetCellType(dm, c, &ct));
9124     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9125     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9126     if (interp == DMPLEX_INTERPOLATED_FULL) {
9127       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9128       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));
9129     }
9130     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9131     for (cl = 0; cl < closureSize * 2; cl += 2) {
9132       const PetscInt p = closure[cl];
9133       if ((p >= vStart) && (p < vEnd)) ++Nv;
9134     }
9135     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9136     /* Special Case: Tensor faces with identified vertices */
9137     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9138       PetscInt unsplit;
9139 
9140       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9141       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9142     }
9143     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));
9144   }
9145   PetscFunctionReturn(PETSC_SUCCESS);
9146 }
9147 
9148 /*@
9149   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9150 
9151   Collective
9152 
9153   Input Parameters:
9154 + dm         - The `DMPLEX` object
9155 - cellHeight - Normally 0
9156 
9157   Level: developer
9158 
9159   Notes:
9160   This is a useful diagnostic when creating meshes programmatically.
9161   This routine is only relevant for meshes that are fully interpolated across all ranks.
9162   It will error out if a partially interpolated mesh is given on some rank.
9163   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9164 
9165   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9166 
9167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9168 @*/
9169 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9170 {
9171   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9172   DMPlexInterpolatedFlag interpEnum;
9173 
9174   PetscFunctionBegin;
9175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9176   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9177   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9178   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9179     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9180     PetscFunctionReturn(PETSC_SUCCESS);
9181   }
9182 
9183   PetscCall(DMGetDimension(dm, &dim));
9184   PetscCall(DMPlexGetDepth(dm, &depth));
9185   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9186   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9187     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9188     for (c = cStart; c < cEnd; ++c) {
9189       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9190       const DMPolytopeType *faceTypes;
9191       DMPolytopeType        ct;
9192       PetscInt              numFaces, coneSize, f;
9193       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9194 
9195       PetscCall(DMPlexGetCellType(dm, c, &ct));
9196       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9197       if (unsplit) continue;
9198       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9199       PetscCall(DMPlexGetCone(dm, c, &cone));
9200       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9201       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9202       for (cl = 0; cl < closureSize * 2; cl += 2) {
9203         const PetscInt p = closure[cl];
9204         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9205       }
9206       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9207       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);
9208       for (f = 0; f < numFaces; ++f) {
9209         DMPolytopeType fct;
9210         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9211 
9212         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9213         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9214         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9215           const PetscInt p = fclosure[cl];
9216           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9217         }
9218         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]);
9219         for (v = 0; v < fnumCorners; ++v) {
9220           if (fclosure[v] != faces[fOff + v]) {
9221             PetscInt v1;
9222 
9223             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9224             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9225             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9226             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9227             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9228             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]);
9229           }
9230         }
9231         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9232         fOff += faceSizes[f];
9233       }
9234       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9235       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9236     }
9237   }
9238   PetscFunctionReturn(PETSC_SUCCESS);
9239 }
9240 
9241 /*@
9242   DMPlexCheckGeometry - Check the geometry of mesh cells
9243 
9244   Input Parameter:
9245 . dm - The `DMPLEX` object
9246 
9247   Level: developer
9248 
9249   Notes:
9250   This is a useful diagnostic when creating meshes programmatically.
9251 
9252   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9253 
9254 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9255 @*/
9256 PetscErrorCode DMPlexCheckGeometry(DM dm)
9257 {
9258   Vec       coordinates;
9259   PetscReal detJ, J[9], refVol = 1.0;
9260   PetscReal vol;
9261   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9262 
9263   PetscFunctionBegin;
9264   PetscCall(DMGetDimension(dm, &dim));
9265   PetscCall(DMGetCoordinateDim(dm, &dE));
9266   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9267   PetscCall(DMPlexGetDepth(dm, &depth));
9268   for (d = 0; d < dim; ++d) refVol *= 2.0;
9269   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9270   /* Make sure local coordinates are created, because that step is collective */
9271   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9272   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9273   for (c = cStart; c < cEnd; ++c) {
9274     DMPolytopeType ct;
9275     PetscInt       unsplit;
9276     PetscBool      ignoreZeroVol = PETSC_FALSE;
9277 
9278     PetscCall(DMPlexGetCellType(dm, c, &ct));
9279     switch (ct) {
9280     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9281     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9282     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9283       ignoreZeroVol = PETSC_TRUE;
9284       break;
9285     default:
9286       break;
9287     }
9288     switch (ct) {
9289     case DM_POLYTOPE_TRI_PRISM:
9290     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9291     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9292     case DM_POLYTOPE_PYRAMID:
9293       continue;
9294     default:
9295       break;
9296     }
9297     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9298     if (unsplit) continue;
9299     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9300     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);
9301     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9302     /* This should work with periodicity since DG coordinates should be used */
9303     if (depth > 1) {
9304       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9305       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);
9306       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9307     }
9308   }
9309   PetscFunctionReturn(PETSC_SUCCESS);
9310 }
9311 
9312 /*@
9313   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9314 
9315   Collective
9316 
9317   Input Parameters:
9318 + dm              - The `DMPLEX` object
9319 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9320 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9321 
9322   Level: developer
9323 
9324   Notes:
9325   This is mainly intended for debugging/testing purposes.
9326 
9327   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9328 
9329   Extra roots can come from periodic cuts, where additional points appear on the boundary
9330 
9331 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9332 @*/
9333 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9334 {
9335   PetscInt           l, nleaves, nroots, overlap;
9336   const PetscInt    *locals;
9337   const PetscSFNode *remotes;
9338   PetscBool          distributed;
9339   MPI_Comm           comm;
9340   PetscMPIInt        rank;
9341 
9342   PetscFunctionBegin;
9343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9344   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9345   else pointSF = dm->sf;
9346   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9347   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9348   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9349   {
9350     PetscMPIInt mpiFlag;
9351 
9352     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9353     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9354   }
9355   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9356   PetscCall(DMPlexIsDistributed(dm, &distributed));
9357   if (!distributed) {
9358     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);
9359     PetscFunctionReturn(PETSC_SUCCESS);
9360   }
9361   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);
9362   PetscCall(DMPlexGetOverlap(dm, &overlap));
9363 
9364   /* Check SF graph is compatible with DMPlex chart */
9365   {
9366     PetscInt pStart, pEnd, maxLeaf;
9367 
9368     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9369     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9370     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9371     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9372   }
9373 
9374   /* Check Point SF has no local points referenced */
9375   for (l = 0; l < nleaves; l++) {
9376     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);
9377   }
9378 
9379   /* Check there are no cells in interface */
9380   if (!overlap) {
9381     PetscInt cellHeight, cStart, cEnd;
9382 
9383     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9384     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9385     for (l = 0; l < nleaves; ++l) {
9386       const PetscInt point = locals ? locals[l] : l;
9387 
9388       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9389     }
9390   }
9391 
9392   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9393   {
9394     const PetscInt *rootdegree;
9395 
9396     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9397     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9398     for (l = 0; l < nleaves; ++l) {
9399       const PetscInt  point = locals ? locals[l] : l;
9400       const PetscInt *cone;
9401       PetscInt        coneSize, c, idx;
9402 
9403       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9404       PetscCall(DMPlexGetCone(dm, point, &cone));
9405       for (c = 0; c < coneSize; ++c) {
9406         if (!rootdegree[cone[c]]) {
9407           if (locals) {
9408             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9409           } else {
9410             idx = (cone[c] < nleaves) ? cone[c] : -1;
9411           }
9412           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9413         }
9414       }
9415     }
9416   }
9417   PetscFunctionReturn(PETSC_SUCCESS);
9418 }
9419 
9420 /*@
9421   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9422 
9423   Input Parameter:
9424 . dm - The `DMPLEX` object
9425 
9426   Level: developer
9427 
9428   Notes:
9429   This is a useful diagnostic when creating meshes programmatically.
9430 
9431   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9432 
9433   Currently does not include `DMPlexCheckCellShape()`.
9434 
9435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9436 @*/
9437 PetscErrorCode DMPlexCheck(DM dm)
9438 {
9439   PetscInt cellHeight;
9440 
9441   PetscFunctionBegin;
9442   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9443   PetscCall(DMPlexCheckSymmetry(dm));
9444   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9445   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9446   PetscCall(DMPlexCheckGeometry(dm));
9447   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9448   PetscCall(DMPlexCheckInterfaceCones(dm));
9449   PetscFunctionReturn(PETSC_SUCCESS);
9450 }
9451 
9452 typedef struct cell_stats {
9453   PetscReal min, max, sum, squaresum;
9454   PetscInt  count;
9455 } cell_stats_t;
9456 
9457 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9458 {
9459   PetscInt i, N = *len;
9460 
9461   for (i = 0; i < N; i++) {
9462     cell_stats_t *A = (cell_stats_t *)a;
9463     cell_stats_t *B = (cell_stats_t *)b;
9464 
9465     B->min = PetscMin(A->min, B->min);
9466     B->max = PetscMax(A->max, B->max);
9467     B->sum += A->sum;
9468     B->squaresum += A->squaresum;
9469     B->count += A->count;
9470   }
9471 }
9472 
9473 /*@
9474   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9475 
9476   Collective
9477 
9478   Input Parameters:
9479 + dm        - The `DMPLEX` object
9480 . output    - If true, statistics will be displayed on `stdout`
9481 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9482 
9483   Level: developer
9484 
9485   Notes:
9486   This is mainly intended for debugging/testing purposes.
9487 
9488   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9489 
9490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9491 @*/
9492 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9493 {
9494   DM           dmCoarse;
9495   cell_stats_t stats, globalStats;
9496   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9497   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9498   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9499   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9500   PetscMPIInt  rank, size;
9501 
9502   PetscFunctionBegin;
9503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9504   stats.min = PETSC_MAX_REAL;
9505   stats.max = PETSC_MIN_REAL;
9506   stats.sum = stats.squaresum = 0.;
9507   stats.count                 = 0;
9508 
9509   PetscCallMPI(MPI_Comm_size(comm, &size));
9510   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9511   PetscCall(DMGetCoordinateDim(dm, &cdim));
9512   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9513   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9514   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9515   for (c = cStart; c < cEnd; c++) {
9516     PetscInt  i;
9517     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9518 
9519     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9520     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9521     for (i = 0; i < PetscSqr(cdim); ++i) {
9522       frobJ += J[i] * J[i];
9523       frobInvJ += invJ[i] * invJ[i];
9524     }
9525     cond2 = frobJ * frobInvJ;
9526     cond  = PetscSqrtReal(cond2);
9527 
9528     stats.min = PetscMin(stats.min, cond);
9529     stats.max = PetscMax(stats.max, cond);
9530     stats.sum += cond;
9531     stats.squaresum += cond2;
9532     stats.count++;
9533     if (output && cond > limit) {
9534       PetscSection coordSection;
9535       Vec          coordsLocal;
9536       PetscScalar *coords = NULL;
9537       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9538 
9539       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9540       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9541       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9542       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9543       for (i = 0; i < Nv / cdim; ++i) {
9544         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9545         for (d = 0; d < cdim; ++d) {
9546           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9547           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9548         }
9549         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9550       }
9551       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9552       for (cl = 0; cl < clSize * 2; cl += 2) {
9553         const PetscInt edge = closure[cl];
9554 
9555         if ((edge >= eStart) && (edge < eEnd)) {
9556           PetscReal len;
9557 
9558           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9559           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9560         }
9561       }
9562       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9563       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9564     }
9565   }
9566   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9567 
9568   if (size > 1) {
9569     PetscMPIInt  blockLengths[2] = {4, 1};
9570     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9571     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9572     MPI_Op       statReduce;
9573 
9574     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9575     PetscCallMPI(MPI_Type_commit(&statType));
9576     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9577     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9578     PetscCallMPI(MPI_Op_free(&statReduce));
9579     PetscCallMPI(MPI_Type_free(&statType));
9580   } else {
9581     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9582   }
9583   if (rank == 0) {
9584     count = globalStats.count;
9585     min   = globalStats.min;
9586     max   = globalStats.max;
9587     mean  = globalStats.sum / globalStats.count;
9588     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9589   }
9590 
9591   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));
9592   PetscCall(PetscFree2(J, invJ));
9593 
9594   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9595   if (dmCoarse) {
9596     PetscBool isplex;
9597 
9598     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9599     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9600   }
9601   PetscFunctionReturn(PETSC_SUCCESS);
9602 }
9603 
9604 /*@
9605   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9606   orthogonal quality below given tolerance.
9607 
9608   Collective
9609 
9610   Input Parameters:
9611 + dm   - The `DMPLEX` object
9612 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9613 - atol - [0, 1] Absolute tolerance for tagging cells.
9614 
9615   Output Parameters:
9616 + OrthQual      - `Vec` containing orthogonal quality per cell
9617 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9618 
9619   Options Database Keys:
9620 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9621 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9622 
9623   Level: intermediate
9624 
9625   Notes:
9626   Orthogonal quality is given by the following formula\:
9627 
9628   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9629 
9630   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
9631   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9632   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9633   calculating the cosine of the angle between these vectors.
9634 
9635   Orthogonal quality ranges from 1 (best) to 0 (worst).
9636 
9637   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9638   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9639 
9640   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9641 
9642 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9643 @*/
9644 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9645 {
9646   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9647   PetscInt              *idx;
9648   PetscScalar           *oqVals;
9649   const PetscScalar     *cellGeomArr, *faceGeomArr;
9650   PetscReal             *ci, *fi, *Ai;
9651   MPI_Comm               comm;
9652   Vec                    cellgeom, facegeom;
9653   DM                     dmFace, dmCell;
9654   IS                     glob;
9655   ISLocalToGlobalMapping ltog;
9656   PetscViewer            vwr;
9657 
9658   PetscFunctionBegin;
9659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9660   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9661   PetscAssertPointer(OrthQual, 4);
9662   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9663   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9664   PetscCall(DMGetDimension(dm, &nc));
9665   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9666   {
9667     DMPlexInterpolatedFlag interpFlag;
9668 
9669     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9670     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9671       PetscMPIInt rank;
9672 
9673       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9674       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9675     }
9676   }
9677   if (OrthQualLabel) {
9678     PetscAssertPointer(OrthQualLabel, 5);
9679     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9680     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9681   } else {
9682     *OrthQualLabel = NULL;
9683   }
9684   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9685   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9686   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9687   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9688   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9689   PetscCall(VecCreate(comm, OrthQual));
9690   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9691   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9692   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9693   PetscCall(VecSetUp(*OrthQual));
9694   PetscCall(ISDestroy(&glob));
9695   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9696   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9697   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9698   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9699   PetscCall(VecGetDM(cellgeom, &dmCell));
9700   PetscCall(VecGetDM(facegeom, &dmFace));
9701   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9702   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9703     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9704     PetscInt         cellarr[2], *adj = NULL;
9705     PetscScalar     *cArr, *fArr;
9706     PetscReal        minvalc = 1.0, minvalf = 1.0;
9707     PetscFVCellGeom *cg;
9708 
9709     idx[cellIter] = cell - cStart;
9710     cellarr[0]    = cell;
9711     /* Make indexing into cellGeom easier */
9712     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9713     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9714     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9715     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9716     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9717       PetscInt         i;
9718       const PetscInt   neigh  = adj[cellneigh];
9719       PetscReal        normci = 0, normfi = 0, normai = 0;
9720       PetscFVCellGeom *cgneigh;
9721       PetscFVFaceGeom *fg;
9722 
9723       /* Don't count ourselves in the neighbor list */
9724       if (neigh == cell) continue;
9725       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9726       cellarr[1] = neigh;
9727       {
9728         PetscInt        numcovpts;
9729         const PetscInt *covpts;
9730 
9731         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9732         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9733         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9734       }
9735 
9736       /* Compute c_i, f_i and their norms */
9737       for (i = 0; i < nc; i++) {
9738         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9739         fi[i] = fg->centroid[i] - cg->centroid[i];
9740         Ai[i] = fg->normal[i];
9741         normci += PetscPowReal(ci[i], 2);
9742         normfi += PetscPowReal(fi[i], 2);
9743         normai += PetscPowReal(Ai[i], 2);
9744       }
9745       normci = PetscSqrtReal(normci);
9746       normfi = PetscSqrtReal(normfi);
9747       normai = PetscSqrtReal(normai);
9748 
9749       /* Normalize and compute for each face-cell-normal pair */
9750       for (i = 0; i < nc; i++) {
9751         ci[i] = ci[i] / normci;
9752         fi[i] = fi[i] / normfi;
9753         Ai[i] = Ai[i] / normai;
9754         /* PetscAbs because I don't know if normals are guaranteed to point out */
9755         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9756         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9757       }
9758       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9759       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9760     }
9761     PetscCall(PetscFree(adj));
9762     PetscCall(PetscFree2(cArr, fArr));
9763     /* Defer to cell if they're equal */
9764     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9765     if (OrthQualLabel) {
9766       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9767     }
9768   }
9769   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9770   PetscCall(VecAssemblyBegin(*OrthQual));
9771   PetscCall(VecAssemblyEnd(*OrthQual));
9772   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9773   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9774   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9775   if (OrthQualLabel) {
9776     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9777   }
9778   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9779   PetscCall(PetscOptionsRestoreViewer(&vwr));
9780   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9781   PetscFunctionReturn(PETSC_SUCCESS);
9782 }
9783 
9784 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9785  * interpolator construction */
9786 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9787 {
9788   PetscSection section, newSection, gsection;
9789   PetscSF      sf;
9790   PetscBool    hasConstraints, ghasConstraints;
9791 
9792   PetscFunctionBegin;
9793   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9794   PetscAssertPointer(odm, 2);
9795   PetscCall(DMGetLocalSection(dm, &section));
9796   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9797   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9798   if (!ghasConstraints) {
9799     PetscCall(PetscObjectReference((PetscObject)dm));
9800     *odm = dm;
9801     PetscFunctionReturn(PETSC_SUCCESS);
9802   }
9803   PetscCall(DMClone(dm, odm));
9804   PetscCall(DMCopyFields(dm, *odm));
9805   PetscCall(DMGetLocalSection(*odm, &newSection));
9806   PetscCall(DMGetPointSF(*odm, &sf));
9807   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9808   PetscCall(DMSetGlobalSection(*odm, gsection));
9809   PetscCall(PetscSectionDestroy(&gsection));
9810   PetscFunctionReturn(PETSC_SUCCESS);
9811 }
9812 
9813 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9814 {
9815   DM        dmco, dmfo;
9816   Mat       interpo;
9817   Vec       rscale;
9818   Vec       cglobalo, clocal;
9819   Vec       fglobal, fglobalo, flocal;
9820   PetscBool regular;
9821 
9822   PetscFunctionBegin;
9823   PetscCall(DMGetFullDM(dmc, &dmco));
9824   PetscCall(DMGetFullDM(dmf, &dmfo));
9825   PetscCall(DMSetCoarseDM(dmfo, dmco));
9826   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9827   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9828   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9829   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9830   PetscCall(DMCreateLocalVector(dmc, &clocal));
9831   PetscCall(VecSet(cglobalo, 0.));
9832   PetscCall(VecSet(clocal, 0.));
9833   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9834   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9835   PetscCall(DMCreateLocalVector(dmf, &flocal));
9836   PetscCall(VecSet(fglobal, 0.));
9837   PetscCall(VecSet(fglobalo, 0.));
9838   PetscCall(VecSet(flocal, 0.));
9839   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9840   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9841   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9842   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9843   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9844   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9845   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9846   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9847   *shift = fglobal;
9848   PetscCall(VecDestroy(&flocal));
9849   PetscCall(VecDestroy(&fglobalo));
9850   PetscCall(VecDestroy(&clocal));
9851   PetscCall(VecDestroy(&cglobalo));
9852   PetscCall(VecDestroy(&rscale));
9853   PetscCall(MatDestroy(&interpo));
9854   PetscCall(DMDestroy(&dmfo));
9855   PetscCall(DMDestroy(&dmco));
9856   PetscFunctionReturn(PETSC_SUCCESS);
9857 }
9858 
9859 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9860 {
9861   PetscObject shifto;
9862   Vec         shift;
9863 
9864   PetscFunctionBegin;
9865   if (!interp) {
9866     Vec rscale;
9867 
9868     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9869     PetscCall(VecDestroy(&rscale));
9870   } else {
9871     PetscCall(PetscObjectReference((PetscObject)interp));
9872   }
9873   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9874   if (!shifto) {
9875     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9876     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9877     shifto = (PetscObject)shift;
9878     PetscCall(VecDestroy(&shift));
9879   }
9880   shift = (Vec)shifto;
9881   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9882   PetscCall(VecAXPY(fineSol, 1.0, shift));
9883   PetscCall(MatDestroy(&interp));
9884   PetscFunctionReturn(PETSC_SUCCESS);
9885 }
9886 
9887 /* Pointwise interpolation
9888      Just code FEM for now
9889      u^f = I u^c
9890      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9891      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9892      I_{ij} = psi^f_i phi^c_j
9893 */
9894 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9895 {
9896   PetscSection gsc, gsf;
9897   PetscInt     m, n;
9898   void        *ctx;
9899   DM           cdm;
9900   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9901 
9902   PetscFunctionBegin;
9903   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9904   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9905   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9906   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9907 
9908   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9909   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9910   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9911   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9912   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9913 
9914   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9915   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9916   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9917   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9918   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9919   if (scaling) {
9920     /* Use naive scaling */
9921     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9922   }
9923   PetscFunctionReturn(PETSC_SUCCESS);
9924 }
9925 
9926 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9927 {
9928   VecScatter ctx;
9929 
9930   PetscFunctionBegin;
9931   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9932   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9933   PetscCall(VecScatterDestroy(&ctx));
9934   PetscFunctionReturn(PETSC_SUCCESS);
9935 }
9936 
9937 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[])
9938 {
9939   const PetscInt Nc = uOff[1] - uOff[0];
9940   PetscInt       c;
9941   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9942 }
9943 
9944 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9945 {
9946   DM           dmc;
9947   PetscDS      ds;
9948   Vec          ones, locmass;
9949   IS           cellIS;
9950   PetscFormKey key;
9951   PetscInt     depth;
9952 
9953   PetscFunctionBegin;
9954   PetscCall(DMClone(dm, &dmc));
9955   PetscCall(DMCopyDisc(dm, dmc));
9956   PetscCall(DMGetDS(dmc, &ds));
9957   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9958   PetscCall(DMCreateGlobalVector(dmc, mass));
9959   PetscCall(DMGetLocalVector(dmc, &ones));
9960   PetscCall(DMGetLocalVector(dmc, &locmass));
9961   PetscCall(DMPlexGetDepth(dmc, &depth));
9962   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9963   PetscCall(VecSet(locmass, 0.0));
9964   PetscCall(VecSet(ones, 1.0));
9965   key.label = NULL;
9966   key.value = 0;
9967   key.field = 0;
9968   key.part  = 0;
9969   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9970   PetscCall(ISDestroy(&cellIS));
9971   PetscCall(VecSet(*mass, 0.0));
9972   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9973   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9974   PetscCall(DMRestoreLocalVector(dmc, &ones));
9975   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9976   PetscCall(DMDestroy(&dmc));
9977   PetscFunctionReturn(PETSC_SUCCESS);
9978 }
9979 
9980 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9981 {
9982   PetscSection gsc, gsf;
9983   PetscInt     m, n;
9984   void        *ctx;
9985   DM           cdm;
9986   PetscBool    regular;
9987 
9988   PetscFunctionBegin;
9989   if (dmFine == dmCoarse) {
9990     DM            dmc;
9991     PetscDS       ds;
9992     PetscWeakForm wf;
9993     Vec           u;
9994     IS            cellIS;
9995     PetscFormKey  key;
9996     PetscInt      depth;
9997 
9998     PetscCall(DMClone(dmFine, &dmc));
9999     PetscCall(DMCopyDisc(dmFine, dmc));
10000     PetscCall(DMGetDS(dmc, &ds));
10001     PetscCall(PetscDSGetWeakForm(ds, &wf));
10002     PetscCall(PetscWeakFormClear(wf));
10003     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10004     PetscCall(DMCreateMatrix(dmc, mass));
10005     PetscCall(DMGetLocalVector(dmc, &u));
10006     PetscCall(DMPlexGetDepth(dmc, &depth));
10007     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10008     PetscCall(MatZeroEntries(*mass));
10009     key.label = NULL;
10010     key.value = 0;
10011     key.field = 0;
10012     key.part  = 0;
10013     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10014     PetscCall(ISDestroy(&cellIS));
10015     PetscCall(DMRestoreLocalVector(dmc, &u));
10016     PetscCall(DMDestroy(&dmc));
10017   } else {
10018     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10019     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10020     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10021     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10022 
10023     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10024     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10025     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10026     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10027 
10028     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10029     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10030     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10031     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10032   }
10033   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10034   PetscFunctionReturn(PETSC_SUCCESS);
10035 }
10036 
10037 /*@
10038   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10039 
10040   Input Parameter:
10041 . dm - The `DMPLEX` object
10042 
10043   Output Parameter:
10044 . regular - The flag
10045 
10046   Level: intermediate
10047 
10048 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10049 @*/
10050 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10051 {
10052   PetscFunctionBegin;
10053   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10054   PetscAssertPointer(regular, 2);
10055   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10056   PetscFunctionReturn(PETSC_SUCCESS);
10057 }
10058 
10059 /*@
10060   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10061 
10062   Input Parameters:
10063 + dm      - The `DMPLEX` object
10064 - regular - The flag
10065 
10066   Level: intermediate
10067 
10068 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10069 @*/
10070 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10071 {
10072   PetscFunctionBegin;
10073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10074   ((DM_Plex *)dm->data)->regularRefinement = regular;
10075   PetscFunctionReturn(PETSC_SUCCESS);
10076 }
10077 
10078 /*@
10079   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10080   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10081 
10082   Not Collective
10083 
10084   Input Parameter:
10085 . dm - The `DMPLEX` object
10086 
10087   Output Parameters:
10088 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10089 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10090 
10091   Level: intermediate
10092 
10093 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10094 @*/
10095 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10096 {
10097   DM_Plex *plex = (DM_Plex *)dm->data;
10098 
10099   PetscFunctionBegin;
10100   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10101   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10102   if (anchorSection) *anchorSection = plex->anchorSection;
10103   if (anchorIS) *anchorIS = plex->anchorIS;
10104   PetscFunctionReturn(PETSC_SUCCESS);
10105 }
10106 
10107 /*@
10108   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10109 
10110   Collective
10111 
10112   Input Parameters:
10113 + dm            - The `DMPLEX` object
10114 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10115                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10116 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10117 
10118   Level: intermediate
10119 
10120   Notes:
10121   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10122   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10123   combination of other points' degrees of freedom.
10124 
10125   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10126   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10127 
10128   The reference counts of `anchorSection` and `anchorIS` are incremented.
10129 
10130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10131 @*/
10132 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10133 {
10134   DM_Plex    *plex = (DM_Plex *)dm->data;
10135   PetscMPIInt result;
10136 
10137   PetscFunctionBegin;
10138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10139   if (anchorSection) {
10140     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10141     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10142     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10143   }
10144   if (anchorIS) {
10145     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10146     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10147     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10148   }
10149 
10150   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10151   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10152   plex->anchorSection = anchorSection;
10153 
10154   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10155   PetscCall(ISDestroy(&plex->anchorIS));
10156   plex->anchorIS = anchorIS;
10157 
10158   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10159     PetscInt        size, a, pStart, pEnd;
10160     const PetscInt *anchors;
10161 
10162     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10163     PetscCall(ISGetLocalSize(anchorIS, &size));
10164     PetscCall(ISGetIndices(anchorIS, &anchors));
10165     for (a = 0; a < size; a++) {
10166       PetscInt p;
10167 
10168       p = anchors[a];
10169       if (p >= pStart && p < pEnd) {
10170         PetscInt dof;
10171 
10172         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10173         if (dof) {
10174           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10175           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10176         }
10177       }
10178     }
10179     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10180   }
10181   /* reset the generic constraints */
10182   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10183   PetscFunctionReturn(PETSC_SUCCESS);
10184 }
10185 
10186 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10187 {
10188   PetscSection anchorSection;
10189   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10190 
10191   PetscFunctionBegin;
10192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10193   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10194   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10195   PetscCall(PetscSectionGetNumFields(section, &numFields));
10196   if (numFields) {
10197     PetscInt f;
10198     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10199 
10200     for (f = 0; f < numFields; f++) {
10201       PetscInt numComp;
10202 
10203       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10204       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10205     }
10206   }
10207   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10208   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10209   pStart = PetscMax(pStart, sStart);
10210   pEnd   = PetscMin(pEnd, sEnd);
10211   pEnd   = PetscMax(pStart, pEnd);
10212   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10213   for (p = pStart; p < pEnd; p++) {
10214     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10215     if (dof) {
10216       PetscCall(PetscSectionGetDof(section, p, &dof));
10217       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10218       for (f = 0; f < numFields; f++) {
10219         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10220         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10221       }
10222     }
10223   }
10224   PetscCall(PetscSectionSetUp(*cSec));
10225   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10226   PetscFunctionReturn(PETSC_SUCCESS);
10227 }
10228 
10229 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10230 {
10231   PetscSection    aSec;
10232   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10233   const PetscInt *anchors;
10234   PetscInt        numFields, f;
10235   IS              aIS;
10236   MatType         mtype;
10237   PetscBool       iscuda, iskokkos;
10238 
10239   PetscFunctionBegin;
10240   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10241   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10242   PetscCall(PetscSectionGetStorageSize(section, &n));
10243   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10244   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10245   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10246   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10247   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10248   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10249   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10250   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10251   else mtype = MATSEQAIJ;
10252   PetscCall(MatSetType(*cMat, mtype));
10253   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10254   PetscCall(ISGetIndices(aIS, &anchors));
10255   /* cSec will be a subset of aSec and section */
10256   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10257   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10258   PetscCall(PetscMalloc1(m + 1, &i));
10259   i[0] = 0;
10260   PetscCall(PetscSectionGetNumFields(section, &numFields));
10261   for (p = pStart; p < pEnd; p++) {
10262     PetscInt rDof, rOff, r;
10263 
10264     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10265     if (!rDof) continue;
10266     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10267     if (numFields) {
10268       for (f = 0; f < numFields; f++) {
10269         annz = 0;
10270         for (r = 0; r < rDof; r++) {
10271           a = anchors[rOff + r];
10272           if (a < sStart || a >= sEnd) continue;
10273           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10274           annz += aDof;
10275         }
10276         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10277         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10278         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10279       }
10280     } else {
10281       annz = 0;
10282       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10283       for (q = 0; q < dof; q++) {
10284         a = anchors[rOff + q];
10285         if (a < sStart || a >= sEnd) continue;
10286         PetscCall(PetscSectionGetDof(section, a, &aDof));
10287         annz += aDof;
10288       }
10289       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10290       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10291       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10292     }
10293   }
10294   nnz = i[m];
10295   PetscCall(PetscMalloc1(nnz, &j));
10296   offset = 0;
10297   for (p = pStart; p < pEnd; p++) {
10298     if (numFields) {
10299       for (f = 0; f < numFields; f++) {
10300         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &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(PetscSectionGetFieldDof(section, a, f, &aDof));
10311             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10312             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10313           }
10314         }
10315       }
10316     } else {
10317       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10318       for (q = 0; q < dof; q++) {
10319         PetscInt rDof, rOff, r;
10320         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10321         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10322         for (r = 0; r < rDof; r++) {
10323           PetscInt s;
10324 
10325           a = anchors[rOff + r];
10326           if (a < sStart || a >= sEnd) continue;
10327           PetscCall(PetscSectionGetDof(section, a, &aDof));
10328           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10329           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10330         }
10331       }
10332     }
10333   }
10334   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10335   PetscCall(PetscFree(i));
10336   PetscCall(PetscFree(j));
10337   PetscCall(ISRestoreIndices(aIS, &anchors));
10338   PetscFunctionReturn(PETSC_SUCCESS);
10339 }
10340 
10341 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10342 {
10343   DM_Plex     *plex = (DM_Plex *)dm->data;
10344   PetscSection anchorSection, section, cSec;
10345   Mat          cMat;
10346 
10347   PetscFunctionBegin;
10348   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10349   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10350   if (anchorSection) {
10351     PetscInt Nf;
10352 
10353     PetscCall(DMGetLocalSection(dm, &section));
10354     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10355     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10356     PetscCall(DMGetNumFields(dm, &Nf));
10357     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10358     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10359     PetscCall(PetscSectionDestroy(&cSec));
10360     PetscCall(MatDestroy(&cMat));
10361   }
10362   PetscFunctionReturn(PETSC_SUCCESS);
10363 }
10364 
10365 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10366 {
10367   IS           subis;
10368   PetscSection section, subsection;
10369 
10370   PetscFunctionBegin;
10371   PetscCall(DMGetLocalSection(dm, &section));
10372   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10373   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10374   /* Create subdomain */
10375   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10376   /* Create submodel */
10377   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10378   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10379   PetscCall(DMSetLocalSection(*subdm, subsection));
10380   PetscCall(PetscSectionDestroy(&subsection));
10381   PetscCall(DMCopyDisc(dm, *subdm));
10382   /* Create map from submodel to global model */
10383   if (is) {
10384     PetscSection    sectionGlobal, subsectionGlobal;
10385     IS              spIS;
10386     const PetscInt *spmap;
10387     PetscInt       *subIndices;
10388     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10389     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10390 
10391     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10392     PetscCall(ISGetIndices(spIS, &spmap));
10393     PetscCall(PetscSectionGetNumFields(section, &Nf));
10394     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10395     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10396     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10397     for (p = pStart; p < pEnd; ++p) {
10398       PetscInt gdof, pSubSize = 0;
10399 
10400       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10401       if (gdof > 0) {
10402         for (f = 0; f < Nf; ++f) {
10403           PetscInt fdof, fcdof;
10404 
10405           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10406           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10407           pSubSize += fdof - fcdof;
10408         }
10409         subSize += pSubSize;
10410         if (pSubSize) {
10411           if (bs < 0) {
10412             bs = pSubSize;
10413           } else if (bs != pSubSize) {
10414             /* Layout does not admit a pointwise block size */
10415             bs = 1;
10416           }
10417         }
10418       }
10419     }
10420     /* Must have same blocksize on all procs (some might have no points) */
10421     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10422     bsLocal[1] = bs;
10423     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10424     if (bsMinMax[0] != bsMinMax[1]) {
10425       bs = 1;
10426     } else {
10427       bs = bsMinMax[0];
10428     }
10429     PetscCall(PetscMalloc1(subSize, &subIndices));
10430     for (p = pStart; p < pEnd; ++p) {
10431       PetscInt gdof, goff;
10432 
10433       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10434       if (gdof > 0) {
10435         const PetscInt point = spmap[p];
10436 
10437         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10438         for (f = 0; f < Nf; ++f) {
10439           PetscInt fdof, fcdof, fc, f2, poff = 0;
10440 
10441           /* Can get rid of this loop by storing field information in the global section */
10442           for (f2 = 0; f2 < f; ++f2) {
10443             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10444             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10445             poff += fdof - fcdof;
10446           }
10447           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10448           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10449           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10450         }
10451       }
10452     }
10453     PetscCall(ISRestoreIndices(spIS, &spmap));
10454     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10455     if (bs > 1) {
10456       /* We need to check that the block size does not come from non-contiguous fields */
10457       PetscInt i, j, set = 1;
10458       for (i = 0; i < subSize; i += bs) {
10459         for (j = 0; j < bs; ++j) {
10460           if (subIndices[i + j] != subIndices[i] + j) {
10461             set = 0;
10462             break;
10463           }
10464         }
10465       }
10466       if (set) PetscCall(ISSetBlockSize(*is, bs));
10467     }
10468     /* Attach nullspace */
10469     for (f = 0; f < Nf; ++f) {
10470       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10471       if ((*subdm)->nullspaceConstructors[f]) break;
10472     }
10473     if (f < Nf) {
10474       MatNullSpace nullSpace;
10475       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10476 
10477       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10478       PetscCall(MatNullSpaceDestroy(&nullSpace));
10479     }
10480   }
10481   PetscFunctionReturn(PETSC_SUCCESS);
10482 }
10483 
10484 /*@
10485   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10486 
10487   Input Parameters:
10488 + dm    - The `DM`
10489 - dummy - unused argument
10490 
10491   Options Database Key:
10492 . -dm_plex_monitor_throughput - Activate the monitor
10493 
10494   Level: developer
10495 
10496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10497 @*/
10498 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10499 {
10500   PetscLogHandler default_handler;
10501 
10502   PetscFunctionBegin;
10503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10504   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10505   if (default_handler) {
10506     PetscLogEvent      event;
10507     PetscEventPerfInfo eventInfo;
10508     PetscReal          cellRate, flopRate;
10509     PetscInt           cStart, cEnd, Nf, N;
10510     const char        *name;
10511 
10512     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10513     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10514     PetscCall(DMGetNumFields(dm, &Nf));
10515     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10516     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10517     N        = (cEnd - cStart) * Nf * eventInfo.count;
10518     flopRate = eventInfo.flops / eventInfo.time;
10519     cellRate = N / eventInfo.time;
10520     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)));
10521   } else {
10522     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.");
10523   }
10524   PetscFunctionReturn(PETSC_SUCCESS);
10525 }
10526