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