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