xref: /petsc/src/dm/impls/plex/plex.c (revision 174dc0c8cee294b82b85e4dd3b331b29396264fc)
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_CreateBoxSFC, 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 /* Logging support */
18 PetscLogEvent DMPLEX_DistributionView, DMPLEX_DistributionLoad;
19 
20 PetscBool  Plexcite       = PETSC_FALSE;
21 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
22                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
23                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
24                             "journal   = {SIAM Journal on Scientific Computing},\n"
25                             "volume    = {38},\n"
26                             "number    = {5},\n"
27                             "pages     = {S143--S155},\n"
28                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
29                             "doi       = {10.1137/15M1026092},\n"
30                             "year      = {2016},\n"
31                             "petsc_uses={DMPlex},\n}\n";
32 
33 PETSC_SINGLE_LIBRARY_INTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
34 
35 /*@
36   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
37 
38   Input Parameter:
39 . dm - The `DMPLEX` object
40 
41   Output Parameter:
42 . simplex - Flag checking for a simplex
43 
44   Level: intermediate
45 
46   Note:
47   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
48   If the mesh has no cells, this returns `PETSC_FALSE`.
49 
50 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
51 @*/
52 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
53 {
54   DMPolytopeType ct;
55   PetscInt       cStart, cEnd;
56 
57   PetscFunctionBegin;
58   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
59   if (cEnd <= cStart) {
60     *simplex = PETSC_FALSE;
61     PetscFunctionReturn(PETSC_SUCCESS);
62   }
63   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
64   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@
69   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
70 
71   Input Parameters:
72 + dm     - The `DMPLEX` object
73 - height - The cell height in the Plex, 0 is the default
74 
75   Output Parameters:
76 + cStart - The first "normal" cell, pass `NULL` if not needed
77 - cEnd   - The upper bound on "normal" cells, pass `NULL` if not needed
78 
79   Level: developer
80 
81   Note:
82   This function requires that tensor cells are ordered last.
83 
84 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
85 @*/
86 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PeOp PetscInt *cStart, PeOp PetscInt *cEnd)
87 {
88   DMLabel         ctLabel;
89   IS              valueIS;
90   const PetscInt *ctypes;
91   PetscBool       found = PETSC_FALSE;
92   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
93 
94   PetscFunctionBegin;
95   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
96   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
97   PetscCall(ISGetLocalSize(valueIS, &Nct));
98   PetscCall(ISGetIndices(valueIS, &ctypes));
99   for (PetscInt t = 0; t < Nct; ++t) {
100     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
101     PetscInt             ctS, ctE, ht;
102 
103     if (ct == DM_POLYTOPE_UNKNOWN) {
104       // If any cells are not typed, just use all cells
105       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
106       break;
107     }
108     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
109     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
110     if (ctS >= ctE) continue;
111     // Check that a point has the right height
112     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
113     if (ht != height) continue;
114     cS    = PetscMin(cS, ctS);
115     cE    = PetscMax(cE, ctE);
116     found = PETSC_TRUE;
117   }
118   if (!Nct || !found) cS = cE = 0;
119   PetscCall(ISDestroy(&valueIS));
120   // Reset label for fast lookup
121   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
122   if (cStart) *cStart = cS;
123   if (cEnd) *cEnd = cE;
124   PetscFunctionReturn(PETSC_SUCCESS);
125 }
126 
127 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
128 {
129   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
130   PetscInt                *sStart, *sEnd;
131   PetscViewerVTKFieldType *ft;
132   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
133   DMLabel                  depthLabel, ctLabel;
134 
135   PetscFunctionBegin;
136   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
137   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
138   PetscCall(DMGetCoordinateDim(dm, &cdim));
139   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
140   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
141   if (field >= 0) {
142     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
143   } else {
144     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
145   }
146 
147   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
148   PetscCall(DMPlexGetDepth(dm, &depth));
149   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
150   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
151   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
152     const DMPolytopeType ict = (DMPolytopeType)c;
153     PetscInt             dep;
154 
155     if (ict == DM_POLYTOPE_FV_GHOST) continue;
156     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
157     if (pStart >= 0) {
158       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
159       if (dep != depth - cellHeight) continue;
160     }
161     if (field >= 0) {
162       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
163     } else {
164       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
165     }
166   }
167 
168   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
169   *types = 0;
170 
171   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
172     if (globalvcdof[c]) ++(*types);
173   }
174 
175   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
176   t = 0;
177   if (globalvcdof[DM_NUM_POLYTOPES]) {
178     sStart[t] = vStart;
179     sEnd[t]   = vEnd;
180     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
181     ++t;
182   }
183 
184   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
185     if (globalvcdof[c]) {
186       const DMPolytopeType ict = (DMPolytopeType)c;
187 
188       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
189       sStart[t] = cStart;
190       sEnd[t]   = cEnd;
191       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
192       ++t;
193     }
194   }
195 
196   if (!*types) {
197     if (field >= 0) {
198       const char *fieldname;
199 
200       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
201       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
202     } else {
203       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
204     }
205   }
206 
207   *ssStart = sStart;
208   *ssEnd   = sEnd;
209   *sft     = ft;
210   PetscFunctionReturn(PETSC_SUCCESS);
211 }
212 
213 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
214 {
215   PetscFunctionBegin;
216   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
221 {
222   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
223   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
224 
225   PetscFunctionBegin;
226   *ft = PETSC_VTK_INVALID;
227   PetscCall(DMGetCoordinateDim(dm, &cdim));
228   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
229   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
230   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
231   if (field >= 0) {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
234   } else {
235     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
236     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
237   }
238   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
239   if (globalvcdof[0]) {
240     *sStart = vStart;
241     *sEnd   = vEnd;
242     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
243     else *ft = PETSC_VTK_POINT_FIELD;
244   } else if (globalvcdof[1]) {
245     *sStart = cStart;
246     *sEnd   = cEnd;
247     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
248     else *ft = PETSC_VTK_CELL_FIELD;
249   } else {
250     if (field >= 0) {
251       const char *fieldname;
252 
253       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
254       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
255     } else {
256       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section\n"));
257     }
258   }
259   PetscFunctionReturn(PETSC_SUCCESS);
260 }
261 
262 /*@
263   DMPlexVecView1D - Plot many 1D solutions on the same line graph
264 
265   Collective
266 
267   Input Parameters:
268 + dm     - The `DMPLEX` object
269 . n      - The number of vectors
270 . u      - The array of local vectors
271 - viewer - The `PetscViewer`
272 
273   Level: advanced
274 
275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
276 @*/
277 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
278 {
279   DM                 cdm;
280   PetscDS            ds;
281   PetscDraw          draw = NULL;
282   PetscDrawLG        lg;
283   Vec                coordinates;
284   const PetscScalar *coords, **sol;
285   PetscReal         *vals;
286   PetscInt          *Nc;
287   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
288   char             **names;
289 
290   PetscFunctionBegin;
291   PetscCall(DMGetCoordinateDM(dm, &cdm));
292   PetscCall(DMGetDS(dm, &ds));
293   PetscCall(PetscDSGetNumFields(ds, &Nf));
294   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
295   PetscCall(PetscDSGetComponents(ds, &Nc));
296 
297   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
298   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
299   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
300 
301   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
302   for (PetscInt i = 0, l = 0; i < n; ++i) {
303     const char *vname;
304 
305     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
306     for (PetscInt f = 0; f < Nf; ++f) {
307       PetscObject disc;
308       const char *fname;
309       char        tmpname[PETSC_MAX_PATH_LEN];
310 
311       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
312       /* TODO Create names for components */
313       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
314         PetscCall(PetscObjectGetName(disc, &fname));
315         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
316         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
317         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
318         PetscCall(PetscStrallocpy(tmpname, &names[l]));
319       }
320     }
321   }
322   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
323   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
324   PetscCall(VecGetArrayRead(coordinates, &coords));
325   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
326   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
327   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
328   PetscSection s;
329   PetscInt     cdof, vdof;
330 
331   PetscCall(DMGetLocalSection(dm, &s));
332   PetscCall(PetscSectionGetDof(s, eStart, &cdof));
333   PetscCall(PetscSectionGetDof(s, vStart, &vdof));
334   if (cdof) {
335     if (vdof) {
336       // P_2
337       PetscInt vFirst = -1;
338 
339       for (PetscInt e = eStart; e < eEnd; ++e) {
340         PetscScalar    *xa, *xb, *svals;
341         const PetscInt *cone;
342 
343         PetscCall(DMPlexGetCone(dm, e, &cone));
344         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
345         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
346         if (e == eStart) vFirst = cone[0];
347         for (PetscInt i = 0; i < n; ++i) {
348           PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
349           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
350         }
351         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
352         if (e == eEnd - 1 && cone[1] != vFirst) {
353           for (PetscInt i = 0; i < n; ++i) {
354             PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
355             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
356           }
357           PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
358           for (PetscInt i = 0; i < n; ++i) {
359             PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
360             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
361           }
362           PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
363         }
364       }
365     } else {
366       // P_0
367       for (PetscInt e = eStart; e < eEnd; ++e) {
368         PetscScalar    *xa, *xb, *svals;
369         const PetscInt *cone;
370 
371         PetscCall(DMPlexGetCone(dm, e, &cone));
372         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
373         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
374         for (PetscInt i = 0; i < n; ++i) {
375           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
376           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
377         }
378         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
379       }
380     }
381   } else if (vdof) {
382     // P_1
383     for (PetscInt v = vStart; v < vEnd; ++v) {
384       PetscScalar *x, *svals;
385 
386       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
387       for (PetscInt i = 0; i < n; ++i) {
388         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
389         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
390       }
391       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
392     }
393   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Discretization not supported");
394   PetscCall(VecRestoreArrayRead(coordinates, &coords));
395   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
396   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
397   PetscCall(PetscFree3(sol, names, vals));
398 
399   PetscCall(PetscDrawLGDraw(lg));
400   PetscCall(PetscDrawLGDestroy(&lg));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
405 {
406   DM dm;
407 
408   PetscFunctionBegin;
409   PetscCall(VecGetDM(u, &dm));
410   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
411   PetscFunctionReturn(PETSC_SUCCESS);
412 }
413 
414 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
415 {
416   DM                 dm;
417   PetscSection       s;
418   PetscDraw          draw, popup;
419   DM                 cdm;
420   PetscSection       coordSection;
421   Vec                coordinates;
422   const PetscScalar *array;
423   PetscReal          lbound[3], ubound[3];
424   PetscReal          vbound[2], time;
425   PetscBool          flg;
426   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
427   const char        *name;
428   char               title[PETSC_MAX_PATH_LEN];
429 
430   PetscFunctionBegin;
431   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
432   PetscCall(VecGetDM(v, &dm));
433   PetscCall(DMGetCoordinateDim(dm, &dim));
434   PetscCall(DMGetLocalSection(dm, &s));
435   PetscCall(PetscSectionGetNumFields(s, &Nf));
436   PetscCall(DMGetCoarsenLevel(dm, &level));
437   PetscCall(DMGetCoordinateDM(dm, &cdm));
438   PetscCall(DMGetLocalSection(cdm, &coordSection));
439   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
440   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
441   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
442 
443   PetscCall(PetscObjectGetName((PetscObject)v, &name));
444   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
445 
446   PetscCall(VecGetLocalSize(coordinates, &N));
447   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
448   PetscCall(PetscDrawClear(draw));
449 
450   /* Could implement something like DMDASelectFields() */
451   for (f = 0; f < Nf; ++f) {
452     DM          fdm = dm;
453     Vec         fv  = v;
454     IS          fis;
455     char        prefix[PETSC_MAX_PATH_LEN];
456     const char *fname;
457 
458     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
459     PetscCall(PetscSectionGetFieldName(s, f, &fname));
460 
461     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
462     else prefix[0] = '\0';
463     if (Nf > 1) {
464       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
465       PetscCall(VecGetSubVector(v, fis, &fv));
466       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
467       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
468     }
469     for (comp = 0; comp < Nc; ++comp, ++w) {
470       PetscInt nmax = 2;
471 
472       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
473       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
474       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
475       PetscCall(PetscDrawSetTitle(draw, title));
476 
477       /* TODO Get max and min only for this component */
478       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
479       if (!flg) {
480         PetscCall(VecMin(fv, NULL, &vbound[0]));
481         PetscCall(VecMax(fv, NULL, &vbound[1]));
482         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
483       }
484 
485       PetscCall(PetscDrawGetPopup(draw, &popup));
486       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
487       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
488       PetscCall(VecGetArrayRead(fv, &array));
489       for (c = cStart; c < cEnd; ++c) {
490         DMPolytopeType     ct;
491         PetscScalar       *coords = NULL, *a = NULL;
492         const PetscScalar *coords_arr;
493         PetscBool          isDG;
494         PetscInt           numCoords;
495         int                color[4] = {-1, -1, -1, -1};
496 
497         PetscCall(DMPlexGetCellType(dm, c, &ct));
498         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
499         if (a) {
500           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
501           color[1] = color[2] = color[3] = color[0];
502         } else {
503           PetscScalar *vals = NULL;
504           PetscInt     numVals, va;
505 
506           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
507           if (!numVals) {
508             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
509             continue;
510           }
511           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);
512           switch (numVals / Nc) {
513           case 1: /* P1 Clamped Segment Prism */
514           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
515             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
516             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
517             break;
518           case 3: /* P1 Triangle */
519           case 4: /* P1 Quadrangle */
520             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
521             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
522             break;
523           case 6: /* P2 Triangle */
524           case 8: /* P2 Quadrangle */
525             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
526             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
527             break;
528           default:
529             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
530           }
531           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
532         }
533         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
534         switch (numCoords) {
535         case 6:
536         case 12: /* Localized triangle */
537           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]));
538           break;
539         case 8:
540         case 16: /* Localized quadrilateral */
541           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
542             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
543           } else {
544             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]));
545             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]));
546           }
547           break;
548         default:
549           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
550         }
551         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
552       }
553       PetscCall(VecRestoreArrayRead(fv, &array));
554       PetscCall(PetscDrawFlush(draw));
555       PetscCall(PetscDrawPause(draw));
556       PetscCall(PetscDrawSave(draw));
557     }
558     if (Nf > 1) {
559       PetscCall(VecRestoreSubVector(v, fis, &fv));
560       PetscCall(ISDestroy(&fis));
561       PetscCall(DMDestroy(&fdm));
562     }
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscDraw draw;
571   PetscInt  dim;
572   PetscBool isnull;
573 
574   PetscFunctionBegin;
575   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
576   PetscCall(PetscDrawIsNull(draw, &isnull));
577   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
578 
579   PetscCall(VecGetDM(v, &dm));
580   PetscCall(DMGetCoordinateDim(dm, &dim));
581   switch (dim) {
582   case 1:
583     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
584     break;
585   case 2:
586     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
587     break;
588   default:
589     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
590   }
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
595 {
596   DM                      dm;
597   Vec                     locv;
598   const char             *name;
599   PetscSection            section;
600   PetscInt                pStart, pEnd;
601   PetscInt                numFields;
602   PetscViewerVTKFieldType ft;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
607   PetscCall(PetscObjectGetName((PetscObject)v, &name));
608   PetscCall(PetscObjectSetName((PetscObject)locv, name));
609   PetscCall(VecCopy(v, locv));
610   PetscCall(DMGetLocalSection(dm, &section));
611   PetscCall(PetscSectionGetNumFields(section, &numFields));
612   if (!numFields) {
613     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
614     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
615   } else {
616     PetscInt f;
617 
618     for (f = 0; f < numFields; f++) {
619       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
620       if (ft == PETSC_VTK_INVALID) continue;
621       PetscCall(PetscObjectReference((PetscObject)locv));
622       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
623     }
624     PetscCall(VecDestroy(&locv));
625   }
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
641   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
642   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
643   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
644     PetscInt    i, numFields;
645     PetscObject fe;
646     PetscBool   fem  = PETSC_FALSE;
647     Vec         locv = v;
648     const char *name;
649     PetscInt    step;
650     PetscReal   time;
651 
652     PetscCall(DMGetNumFields(dm, &numFields));
653     for (i = 0; i < numFields; i++) {
654       PetscCall(DMGetField(dm, i, NULL, &fe));
655       if (fe->classid == PETSCFE_CLASSID) {
656         fem = PETSC_TRUE;
657         break;
658       }
659     }
660     if (fem) {
661       PetscObject isZero;
662 
663       PetscCall(DMGetLocalVector(dm, &locv));
664       PetscCall(PetscObjectGetName((PetscObject)v, &name));
665       PetscCall(PetscObjectSetName((PetscObject)locv, name));
666       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
667       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
668       PetscCall(VecCopy(v, locv));
669       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
670       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
671     }
672     if (isvtk) {
673       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
674     } else if (ishdf5) {
675 #if defined(PETSC_HAVE_HDF5)
676       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
677 #else
678       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680     } else if (isdraw) {
681       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
682     } else if (ispython) {
683       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
684     } else if (isglvis) {
685       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
686       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
687       PetscCall(VecView_GLVis(locv, viewer));
688     } else if (iscgns) {
689 #if defined(PETSC_HAVE_CGNS)
690       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
691 #else
692       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
693 #endif
694     }
695     if (fem) {
696       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
697       PetscCall(DMRestoreLocalVector(dm, &locv));
698     }
699   } else {
700     PetscBool isseq;
701 
702     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
703     if (isseq) PetscCall(VecView_Seq(v, viewer));
704     else PetscCall(VecView_MPI(v, viewer));
705   }
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
710 {
711   DM        dm;
712   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
713 
714   PetscFunctionBegin;
715   PetscCall(VecGetDM(v, &dm));
716   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
723   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
724   if (isvtk || isdraw || isglvis || iscgns || ispython) {
725     Vec         locv;
726     PetscObject isZero;
727     const char *name;
728 
729     PetscCall(DMGetLocalVector(dm, &locv));
730     PetscCall(PetscObjectGetName((PetscObject)v, &name));
731     PetscCall(PetscObjectSetName((PetscObject)locv, name));
732     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
733     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
734     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
735     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
736     PetscCall(VecView_Plex_Local(locv, viewer));
737     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
738     PetscCall(DMRestoreLocalVector(dm, &locv));
739   } else if (ishdf5) {
740 #if defined(PETSC_HAVE_HDF5)
741     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
742 #else
743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
744 #endif
745   } else if (isexodusii) {
746 #if defined(PETSC_HAVE_EXODUSII)
747     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
748 #else
749     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
750 #endif
751   } else {
752     PetscBool isseq;
753 
754     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
755     if (isseq) PetscCall(VecView_Seq(v, viewer));
756     else PetscCall(VecView_MPI(v, viewer));
757   }
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
762 {
763   DM                dm;
764   MPI_Comm          comm;
765   PetscViewerFormat format;
766   Vec               v;
767   PetscBool         isvtk, ishdf5;
768 
769   PetscFunctionBegin;
770   PetscCall(VecGetDM(originalv, &dm));
771   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
772   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
773   PetscCall(PetscViewerGetFormat(viewer, &format));
774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
776   if (format == PETSC_VIEWER_NATIVE) {
777     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
778     /* this need a better fix */
779     if (dm->useNatural) {
780       if (dm->sfNatural) {
781         const char *vecname;
782         PetscInt    n, nroots;
783 
784         PetscCall(VecGetLocalSize(originalv, &n));
785         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
786         if (n == nroots) {
787           PetscCall(DMPlexCreateNaturalVector(dm, &v));
788           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
789           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
790           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
791           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
792         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
793       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
794     } else v = originalv;
795   } else v = originalv;
796 
797   if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
800 #else
801     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
802 #endif
803   } else if (isvtk) {
804     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
805   } else {
806     PetscBool isseq;
807 
808     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
809     if (isseq) PetscCall(VecView_Seq(v, viewer));
810     else PetscCall(VecView_MPI(v, viewer));
811   }
812   if (v != originalv) PetscCall(VecDestroy(&v));
813   PetscFunctionReturn(PETSC_SUCCESS);
814 }
815 
816 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
817 {
818   DM        dm;
819   PetscBool ishdf5;
820 
821   PetscFunctionBegin;
822   PetscCall(VecGetDM(v, &dm));
823   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
824   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
825   if (ishdf5) {
826     DM          dmBC;
827     Vec         gv;
828     const char *name;
829 
830     PetscCall(DMGetOutputDM(dm, &dmBC));
831     PetscCall(DMGetGlobalVector(dmBC, &gv));
832     PetscCall(PetscObjectGetName((PetscObject)v, &name));
833     PetscCall(PetscObjectSetName((PetscObject)gv, name));
834     PetscCall(VecLoad_Default(gv, viewer));
835     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
836     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
837     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
838   } else PetscCall(VecLoad_Default(v, viewer));
839   PetscFunctionReturn(PETSC_SUCCESS);
840 }
841 
842 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
843 {
844   DM        dm;
845   PetscBool ishdf5, isexodusii, iscgns;
846 
847   PetscFunctionBegin;
848   PetscCall(VecGetDM(v, &dm));
849   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
850   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
851   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
852   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
853   if (ishdf5) {
854 #if defined(PETSC_HAVE_HDF5)
855     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
856 #else
857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
858 #endif
859   } else if (isexodusii) {
860 #if defined(PETSC_HAVE_EXODUSII)
861     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
862 #else
863     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
864 #endif
865   } else if (iscgns) {
866 #if defined(PETSC_HAVE_CGNS)
867     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
868 #else
869     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
870 #endif
871   } else PetscCall(VecLoad_Default(v, viewer));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
876 {
877   DM                dm;
878   PetscViewerFormat format;
879   PetscBool         ishdf5;
880 
881   PetscFunctionBegin;
882   PetscCall(VecGetDM(originalv, &dm));
883   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
884   PetscCall(PetscViewerGetFormat(viewer, &format));
885   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
886   if (format == PETSC_VIEWER_NATIVE) {
887     if (dm->useNatural) {
888       if (dm->sfNatural) {
889         if (ishdf5) {
890 #if defined(PETSC_HAVE_HDF5)
891           Vec         v;
892           const char *vecname;
893 
894           PetscCall(DMPlexCreateNaturalVector(dm, &v));
895           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
896           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
897           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
898           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
899           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
900           PetscCall(VecDestroy(&v));
901 #else
902           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
903 #endif
904         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
905       }
906     } else PetscCall(VecLoad_Default(originalv, viewer));
907   }
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
912 {
913   PetscSection       coordSection;
914   Vec                coordinates;
915   DMLabel            depthLabel, celltypeLabel;
916   const char        *name[4];
917   const PetscScalar *a;
918   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
919 
920   PetscFunctionBegin;
921   PetscCall(DMGetDimension(dm, &dim));
922   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
923   PetscCall(DMGetCoordinateSection(dm, &coordSection));
924   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
925   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
926   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
927   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
928   PetscCall(VecGetArrayRead(coordinates, &a));
929   name[0]       = "vertex";
930   name[1]       = "edge";
931   name[dim - 1] = "face";
932   name[dim]     = "cell";
933   for (c = cStart; c < cEnd; ++c) {
934     PetscInt *closure = NULL;
935     PetscInt  closureSize, cl, ct;
936 
937     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
938     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
939     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
940     PetscCall(PetscViewerASCIIPushTab(viewer));
941     for (cl = 0; cl < closureSize * 2; cl += 2) {
942       PetscInt point = closure[cl], depth, dof, off, d, p;
943 
944       if ((point < pStart) || (point >= pEnd)) continue;
945       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
946       if (!dof) continue;
947       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
948       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
949       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
950       for (p = 0; p < dof / dim; ++p) {
951         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
952         for (d = 0; d < dim; ++d) {
953           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
954           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
955         }
956         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
957       }
958       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
959     }
960     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
961     PetscCall(PetscViewerASCIIPopTab(viewer));
962   }
963   PetscCall(VecRestoreArrayRead(coordinates, &a));
964   PetscFunctionReturn(PETSC_SUCCESS);
965 }
966 
967 typedef enum {
968   CS_CARTESIAN,
969   CS_POLAR,
970   CS_CYLINDRICAL,
971   CS_SPHERICAL
972 } CoordSystem;
973 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
974 
975 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
976 {
977   PetscInt i;
978 
979   PetscFunctionBegin;
980   if (dim > 3) {
981     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
982   } else {
983     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
984 
985     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
986     switch (cs) {
987     case CS_CARTESIAN:
988       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
989       break;
990     case CS_POLAR:
991       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
992       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
993       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
994       break;
995     case CS_CYLINDRICAL:
996       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
997       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
998       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
999       trcoords[2] = coords[2];
1000       break;
1001     case CS_SPHERICAL:
1002       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
1003       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1004       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1005       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1006       break;
1007     }
1008     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1009   }
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1014 {
1015   DM_Plex          *mesh = (DM_Plex *)dm->data;
1016   DM                cdm, cdmCell;
1017   PetscSection      coordSection, coordSectionCell;
1018   Vec               coordinates, coordinatesCell;
1019   PetscViewerFormat format;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscViewerGetFormat(viewer, &format));
1023   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1024     const char *name;
1025     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1026     PetscInt    pStart, pEnd, p, numLabels, l;
1027     PetscMPIInt rank, size;
1028 
1029     PetscCall(DMGetCoordinateDM(dm, &cdm));
1030     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1031     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1032     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1033     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1034     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1035     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1036     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1037     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1039     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1040     PetscCall(DMGetDimension(dm, &dim));
1041     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1042     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1043     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1044     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1045     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1046     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1047     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1048     for (p = pStart; p < pEnd; ++p) {
1049       PetscInt dof, off, s;
1050 
1051       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1052       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1053       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1054     }
1055     PetscCall(PetscViewerFlush(viewer));
1056     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1057     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1058     for (p = pStart; p < pEnd; ++p) {
1059       PetscInt dof, off, c;
1060 
1061       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1062       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1063       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]));
1064     }
1065     PetscCall(PetscViewerFlush(viewer));
1066     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1067     if (coordSection && coordinates) {
1068       CoordSystem        cs = CS_CARTESIAN;
1069       const PetscScalar *array, *arrayCell = NULL;
1070       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1071       PetscMPIInt        rank;
1072       const char        *name;
1073 
1074       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1075       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1076       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1077       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1078       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1079       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1080       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1081       pStart = PetscMin(pvStart, pcStart);
1082       pEnd   = PetscMax(pvEnd, pcEnd);
1083       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1084       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1085       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1086       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1087 
1088       PetscCall(VecGetArrayRead(coordinates, &array));
1089       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1090       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1091       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1092       for (p = pStart; p < pEnd; ++p) {
1093         PetscInt dof, off;
1094 
1095         if (p >= pvStart && p < pvEnd) {
1096           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1097           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1098           if (dof) {
1099             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1100             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1101             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1102           }
1103         }
1104         if (cdmCell && p >= pcStart && p < pcEnd) {
1105           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1106           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1107           if (dof) {
1108             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1109             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1110             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1111           }
1112         }
1113       }
1114       PetscCall(PetscViewerFlush(viewer));
1115       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1116       PetscCall(VecRestoreArrayRead(coordinates, &array));
1117       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1118     }
1119     PetscCall(DMGetNumLabels(dm, &numLabels));
1120     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1121     for (l = 0; l < numLabels; ++l) {
1122       DMLabel     label;
1123       PetscBool   isdepth;
1124       const char *name;
1125 
1126       PetscCall(DMGetLabelName(dm, l, &name));
1127       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1128       if (isdepth) continue;
1129       PetscCall(DMGetLabel(dm, name, &label));
1130       PetscCall(DMLabelView(label, viewer));
1131     }
1132     if (size > 1) {
1133       PetscSF sf;
1134 
1135       PetscCall(DMGetPointSF(dm, &sf));
1136       PetscCall(PetscSFView(sf, viewer));
1137     }
1138     if (mesh->periodic.face_sfs)
1139       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1140     PetscCall(PetscViewerFlush(viewer));
1141   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1142     const char  *name, *color;
1143     const char  *defcolors[3]  = {"gray", "orange", "green"};
1144     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1145     char         lname[PETSC_MAX_PATH_LEN];
1146     PetscReal    scale      = 2.0;
1147     PetscReal    tikzscale  = 1.0;
1148     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1149     double       tcoords[3];
1150     PetscScalar *coords;
1151     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;
1152     PetscMPIInt  rank, size;
1153     char       **names, **colors, **lcolors;
1154     PetscBool    flg, lflg;
1155     PetscBT      wp = NULL;
1156     PetscInt     pEnd, pStart;
1157 
1158     PetscCall(DMGetCoordinateDM(dm, &cdm));
1159     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1160     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1161     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1162     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1163     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1164     PetscCall(DMGetDimension(dm, &dim));
1165     PetscCall(DMPlexGetDepth(dm, &depth));
1166     PetscCall(DMGetNumLabels(dm, &numLabels));
1167     numLabels  = PetscMax(numLabels, 10);
1168     numColors  = 10;
1169     numLColors = 10;
1170     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1171     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1172     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1173     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1174     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1175     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1176     n = 4;
1177     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1178     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1179     n = 4;
1180     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1181     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1182     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1183     if (!useLabels) numLabels = 0;
1184     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1185     if (!useColors) {
1186       numColors = 3;
1187       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1188     }
1189     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1190     if (!useColors) {
1191       numLColors = 4;
1192       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1193     }
1194     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1195     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1196     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1197     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1198     if (depth < dim) plotEdges = PETSC_FALSE;
1199     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1200 
1201     /* filter points with labelvalue != labeldefaultvalue */
1202     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1203     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1204     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1205     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1206     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1207     if (lflg) {
1208       DMLabel lbl;
1209 
1210       PetscCall(DMGetLabel(dm, lname, &lbl));
1211       if (lbl) {
1212         PetscInt val, defval;
1213 
1214         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1215         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1216         for (c = pStart; c < pEnd; c++) {
1217           PetscInt *closure = NULL;
1218           PetscInt  closureSize;
1219 
1220           PetscCall(DMLabelGetValue(lbl, c, &val));
1221           if (val == defval) continue;
1222 
1223           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229 
1230     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1231     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1232     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1233     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1234 \\documentclass[tikz]{standalone}\n\n\
1235 \\usepackage{pgflibraryshapes}\n\
1236 \\usetikzlibrary{backgrounds}\n\
1237 \\usetikzlibrary{arrows}\n\
1238 \\begin{document}\n"));
1239     if (size > 1) {
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1241       for (p = 0; p < size; ++p) {
1242         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1243         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1244       }
1245       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1246     }
1247     if (drawHasse) {
1248       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1249 
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1264       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1265       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1266     }
1267     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1268 
1269     /* Plot vertices */
1270     PetscCall(VecGetArray(coordinates, &coords));
1271     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1272     for (v = vStart; v < vEnd; ++v) {
1273       PetscInt  off, dof, d;
1274       PetscBool isLabeled = PETSC_FALSE;
1275 
1276       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1277       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1278       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1279       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1280       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1281       for (d = 0; d < dof; ++d) {
1282         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1283         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1284       }
1285       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1286       if (dim == 3) {
1287         PetscReal tmp = tcoords[1];
1288         tcoords[1]    = tcoords[2];
1289         tcoords[2]    = -tmp;
1290       }
1291       for (d = 0; d < dof; ++d) {
1292         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1293         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1294       }
1295       if (drawHasse) color = colors[0 % numColors];
1296       else color = colors[rank % numColors];
1297       for (l = 0; l < numLabels; ++l) {
1298         PetscInt val;
1299         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1300         if (val >= 0) {
1301           color     = lcolors[l % numLColors];
1302           isLabeled = PETSC_TRUE;
1303           break;
1304         }
1305       }
1306       if (drawNumbers[0]) {
1307         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1308       } else if (drawColors[0]) {
1309         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1310       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1311     }
1312     PetscCall(VecRestoreArray(coordinates, &coords));
1313     PetscCall(PetscViewerFlush(viewer));
1314     /* Plot edges */
1315     if (plotEdges) {
1316       PetscCall(VecGetArray(coordinates, &coords));
1317       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1318       for (e = eStart; e < eEnd; ++e) {
1319         const PetscInt *cone;
1320         PetscInt        coneSize, offA, offB, dof, d;
1321 
1322         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1323         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1324         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1325         PetscCall(DMPlexGetCone(dm, e, &cone));
1326         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1327         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1328         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1329         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1330         for (d = 0; d < dof; ++d) {
1331           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1332           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1333         }
1334         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1335         if (dim == 3) {
1336           PetscReal tmp = tcoords[1];
1337           tcoords[1]    = tcoords[2];
1338           tcoords[2]    = -tmp;
1339         }
1340         for (d = 0; d < dof; ++d) {
1341           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1342           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1343         }
1344         if (drawHasse) color = colors[1 % numColors];
1345         else color = colors[rank % numColors];
1346         for (l = 0; l < numLabels; ++l) {
1347           PetscInt val;
1348           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1349           if (val >= 0) {
1350             color = lcolors[l % numLColors];
1351             break;
1352           }
1353         }
1354         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1355       }
1356       PetscCall(VecRestoreArray(coordinates, &coords));
1357       PetscCall(PetscViewerFlush(viewer));
1358       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1359     }
1360     /* Plot cells */
1361     if (dim == 3 || !drawNumbers[1]) {
1362       for (e = eStart; e < eEnd; ++e) {
1363         const PetscInt *cone;
1364 
1365         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1366         color = colors[rank % numColors];
1367         for (l = 0; l < numLabels; ++l) {
1368           PetscInt val;
1369           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1370           if (val >= 0) {
1371             color = lcolors[l % numLColors];
1372             break;
1373           }
1374         }
1375         PetscCall(DMPlexGetCone(dm, e, &cone));
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1377       }
1378     } else {
1379       DMPolytopeType ct;
1380 
1381       /* Drawing a 2D polygon */
1382       for (c = cStart; c < cEnd; ++c) {
1383         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1384         PetscCall(DMPlexGetCellType(dm, c, &ct));
1385         if (DMPolytopeTypeIsHybrid(ct)) {
1386           const PetscInt *cone;
1387           PetscInt        coneSize, e;
1388 
1389           PetscCall(DMPlexGetCone(dm, c, &cone));
1390           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1391           for (e = 0; e < coneSize; ++e) {
1392             const PetscInt *econe;
1393 
1394             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1395             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));
1396           }
1397         } else {
1398           PetscInt *closure = NULL;
1399           PetscInt  closureSize, Nv = 0, v;
1400 
1401           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1402           for (p = 0; p < closureSize * 2; p += 2) {
1403             const PetscInt point = closure[p];
1404 
1405             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1406           }
1407           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1408           for (v = 0; v <= Nv; ++v) {
1409             const PetscInt vertex = closure[v % Nv];
1410 
1411             if (v > 0) {
1412               if (plotEdges) {
1413                 const PetscInt *edge;
1414                 PetscInt        endpoints[2], ne;
1415 
1416                 endpoints[0] = closure[v - 1];
1417                 endpoints[1] = vertex;
1418                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1419                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1420                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1421                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1422               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1423             }
1424             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1425           }
1426           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1427           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1428         }
1429       }
1430     }
1431     for (c = cStart; c < cEnd; ++c) {
1432       double             ccoords[3] = {0.0, 0.0, 0.0};
1433       PetscBool          isLabeled  = PETSC_FALSE;
1434       PetscScalar       *cellCoords = NULL;
1435       const PetscScalar *array;
1436       PetscInt           numCoords, cdim, d;
1437       PetscBool          isDG;
1438 
1439       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1440       PetscCall(DMGetCoordinateDim(dm, &cdim));
1441       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1442       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1443       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1444       for (p = 0; p < numCoords / cdim; ++p) {
1445         for (d = 0; d < cdim; ++d) {
1446           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1447           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1448         }
1449         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1450         if (cdim == 3) {
1451           PetscReal tmp = tcoords[1];
1452           tcoords[1]    = tcoords[2];
1453           tcoords[2]    = -tmp;
1454         }
1455         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1456       }
1457       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1458       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1459       for (d = 0; d < cdim; ++d) {
1460         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1461         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1462       }
1463       if (drawHasse) color = colors[depth % numColors];
1464       else color = colors[rank % numColors];
1465       for (l = 0; l < numLabels; ++l) {
1466         PetscInt val;
1467         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1468         if (val >= 0) {
1469           color     = lcolors[l % numLColors];
1470           isLabeled = PETSC_TRUE;
1471           break;
1472         }
1473       }
1474       if (drawNumbers[dim]) {
1475         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1476       } else if (drawColors[dim]) {
1477         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1478       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1479     }
1480     if (drawHasse) {
1481       int height = 0;
1482 
1483       color = colors[depth % numColors];
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1485       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1487       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1489 
1490       if (depth > 2) {
1491         color = colors[1 % numColors];
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1496         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1497       }
1498 
1499       color = colors[1 % numColors];
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1501       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1502       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1503       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1504       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1505 
1506       color = colors[0 % numColors];
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1508       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1509       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1510       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1511       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1512 
1513       for (p = pStart; p < pEnd; ++p) {
1514         const PetscInt *cone;
1515         PetscInt        coneSize, cp;
1516 
1517         PetscCall(DMPlexGetCone(dm, p, &cone));
1518         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1519         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1520       }
1521     }
1522     PetscCall(PetscViewerFlush(viewer));
1523     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1524     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1526     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1527     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1528     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1529     PetscCall(PetscFree3(names, colors, lcolors));
1530     PetscCall(PetscBTDestroy(&wp));
1531   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1532     Vec                    cown, acown;
1533     VecScatter             sct;
1534     ISLocalToGlobalMapping g2l;
1535     IS                     gid, acis;
1536     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1537     MPI_Group              ggroup, ngroup;
1538     PetscScalar           *array, nid;
1539     const PetscInt        *idxs;
1540     PetscInt              *idxs2, *start, *adjacency, *work;
1541     PetscInt64             lm[3], gm[3];
1542     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1543     PetscMPIInt            d1, d2, rank;
1544 
1545     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1546     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1547 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1548     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1549 #endif
1550     if (ncomm != MPI_COMM_NULL) {
1551       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1552       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1553       d1 = 0;
1554       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1555       nid = d2;
1556       PetscCallMPI(MPI_Group_free(&ggroup));
1557       PetscCallMPI(MPI_Group_free(&ngroup));
1558       PetscCallMPI(MPI_Comm_free(&ncomm));
1559     } else nid = 0.0;
1560 
1561     /* Get connectivity */
1562     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1563     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1564 
1565     /* filter overlapped local cells */
1566     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1567     PetscCall(ISGetIndices(gid, &idxs));
1568     PetscCall(ISGetLocalSize(gid, &cum));
1569     PetscCall(PetscMalloc1(cum, &idxs2));
1570     for (c = cStart, cum = 0; c < cEnd; c++) {
1571       if (idxs[c - cStart] < 0) continue;
1572       idxs2[cum++] = idxs[c - cStart];
1573     }
1574     PetscCall(ISRestoreIndices(gid, &idxs));
1575     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1576     PetscCall(ISDestroy(&gid));
1577     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1578 
1579     /* support for node-aware cell locality */
1580     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1581     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1582     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1583     PetscCall(VecGetArray(cown, &array));
1584     for (c = 0; c < numVertices; c++) array[c] = nid;
1585     PetscCall(VecRestoreArray(cown, &array));
1586     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1587     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1588     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1589     PetscCall(ISDestroy(&acis));
1590     PetscCall(VecScatterDestroy(&sct));
1591     PetscCall(VecDestroy(&cown));
1592 
1593     /* compute edgeCut */
1594     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1595     PetscCall(PetscMalloc1(cum, &work));
1596     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1597     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1598     PetscCall(ISDestroy(&gid));
1599     PetscCall(VecGetArray(acown, &array));
1600     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1601       PetscInt totl;
1602 
1603       totl = start[c + 1] - start[c];
1604       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1605       for (i = 0; i < totl; i++) {
1606         if (work[i] < 0) {
1607           ect += 1;
1608           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1609         }
1610       }
1611     }
1612     PetscCall(PetscFree(work));
1613     PetscCall(VecRestoreArray(acown, &array));
1614     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1615     lm[1] = -numVertices;
1616     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1617     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1618     lm[0] = ect;                     /* edgeCut */
1619     lm[1] = ectn;                    /* node-aware edgeCut */
1620     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1621     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1622     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1623 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1624     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1625 #else
1626     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1627 #endif
1628     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1629     PetscCall(PetscFree(start));
1630     PetscCall(PetscFree(adjacency));
1631     PetscCall(VecDestroy(&acown));
1632   } else {
1633     const char    *name;
1634     PetscInt      *sizes, *hybsizes, *ghostsizes;
1635     PetscInt       locDepth, depth, cellHeight, dim, d;
1636     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1637     PetscInt       numLabels, l, maxSize = 17;
1638     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1639     MPI_Comm       comm;
1640     PetscMPIInt    size, rank;
1641 
1642     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1643     PetscCallMPI(MPI_Comm_size(comm, &size));
1644     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1645     PetscCall(DMGetDimension(dm, &dim));
1646     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1647     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1648     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1649     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1650     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1651     PetscCall(DMPlexGetDepth(dm, &locDepth));
1652     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1653     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1654     gcNum = gcEnd - gcStart;
1655     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1656     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1657     for (d = 0; d <= depth; d++) {
1658       PetscInt Nc[2] = {0, 0}, ict;
1659 
1660       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1661       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1662       ict = ct0;
1663       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1664       ct0 = (DMPolytopeType)ict;
1665       for (p = pStart; p < pEnd; ++p) {
1666         DMPolytopeType ct;
1667 
1668         PetscCall(DMPlexGetCellType(dm, p, &ct));
1669         if (ct == ct0) ++Nc[0];
1670         else ++Nc[1];
1671       }
1672       if (size < maxSize) {
1673         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1674         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1675         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1676         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1677         for (p = 0; p < size; ++p) {
1678           if (rank == 0) {
1679             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1680             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1681             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1682           }
1683         }
1684       } else {
1685         PetscInt locMinMax[2];
1686 
1687         locMinMax[0] = Nc[0] + Nc[1];
1688         locMinMax[1] = Nc[0] + Nc[1];
1689         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1690         locMinMax[0] = Nc[1];
1691         locMinMax[1] = Nc[1];
1692         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1693         if (d == depth) {
1694           locMinMax[0] = gcNum;
1695           locMinMax[1] = gcNum;
1696           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1697         }
1698         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1699         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1700         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1701         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1702       }
1703       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1704     }
1705     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1706     {
1707       const PetscReal *maxCell;
1708       const PetscReal *L;
1709       PetscBool        localized;
1710 
1711       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1712       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1713       if (L || localized) {
1714         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1715         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1716         if (L) {
1717           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1718           for (d = 0; d < dim; ++d) {
1719             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1720             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1721           }
1722           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1723         }
1724         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1725         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1726       }
1727     }
1728     PetscCall(DMGetNumLabels(dm, &numLabels));
1729     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1730     for (l = 0; l < numLabels; ++l) {
1731       DMLabel     label;
1732       const char *name;
1733       PetscInt   *values;
1734       PetscInt    numValues, v;
1735 
1736       PetscCall(DMGetLabelName(dm, l, &name));
1737       PetscCall(DMGetLabel(dm, name, &label));
1738       PetscCall(DMLabelGetNumValues(label, &numValues));
1739       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1740 
1741       { // Extract array of DMLabel values so it can be sorted
1742         IS              is_values;
1743         const PetscInt *is_values_local = NULL;
1744 
1745         PetscCall(DMLabelGetValueIS(label, &is_values));
1746         PetscCall(ISGetIndices(is_values, &is_values_local));
1747         PetscCall(PetscMalloc1(numValues, &values));
1748         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1749         PetscCall(PetscSortInt(numValues, values));
1750         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1751         PetscCall(ISDestroy(&is_values));
1752       }
1753       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1754       for (v = 0; v < numValues; ++v) {
1755         PetscInt size;
1756 
1757         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1758         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1759         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1760       }
1761       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1762       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1763       PetscCall(PetscFree(values));
1764     }
1765     {
1766       char    **labelNames;
1767       PetscInt  Nl = numLabels;
1768       PetscBool flg;
1769 
1770       PetscCall(PetscMalloc1(Nl, &labelNames));
1771       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1772       for (l = 0; l < Nl; ++l) {
1773         DMLabel label;
1774 
1775         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1776         if (flg) {
1777           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1778           PetscCall(DMLabelView(label, viewer));
1779         }
1780         PetscCall(PetscFree(labelNames[l]));
1781       }
1782       PetscCall(PetscFree(labelNames));
1783     }
1784     /* If no fields are specified, people do not want to see adjacency */
1785     if (dm->Nf) {
1786       PetscInt f;
1787 
1788       for (f = 0; f < dm->Nf; ++f) {
1789         const char *name;
1790 
1791         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1792         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1793         PetscCall(PetscViewerASCIIPushTab(viewer));
1794         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1795         if (dm->fields[f].adjacency[0]) {
1796           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1797           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1798         } else {
1799           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1800           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1801         }
1802         PetscCall(PetscViewerASCIIPopTab(viewer));
1803       }
1804     }
1805     DMPlexTransform tr;
1806 
1807     PetscCall(DMPlexGetTransform(dm, &tr));
1808     if (tr) {
1809       PetscCall(PetscViewerASCIIPushTab(viewer));
1810       PetscCall(PetscViewerASCIIPrintf(viewer, "Created using transform:\n"));
1811       PetscCall(DMPlexTransformView(tr, viewer));
1812       PetscCall(PetscViewerASCIIPopTab(viewer));
1813     }
1814     PetscCall(DMGetCoarseDM(dm, &cdm));
1815     if (cdm) {
1816       PetscCall(PetscViewerASCIIPushTab(viewer));
1817       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1818       PetscCall(DMPlexView_Ascii(cdm, viewer));
1819       PetscCall(PetscViewerASCIIPopTab(viewer));
1820     }
1821   }
1822   PetscFunctionReturn(PETSC_SUCCESS);
1823 }
1824 
1825 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1826 {
1827   DMPolytopeType ct;
1828   PetscMPIInt    rank;
1829   PetscInt       cdim;
1830 
1831   PetscFunctionBegin;
1832   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1833   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1834   PetscCall(DMGetCoordinateDim(dm, &cdim));
1835   switch (ct) {
1836   case DM_POLYTOPE_SEGMENT:
1837   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1838     switch (cdim) {
1839     case 1: {
1840       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1841       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1842 
1843       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1844       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1845       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1846     } break;
1847     case 2: {
1848       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1849       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1850       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1851 
1852       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1853       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));
1854       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));
1855     } break;
1856     default:
1857       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1858     }
1859     break;
1860   case DM_POLYTOPE_TRIANGLE:
1861     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));
1862     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1863     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1864     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1865     break;
1866   case DM_POLYTOPE_QUADRILATERAL:
1867     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));
1868     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));
1869     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1870     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1871     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1872     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1873     break;
1874   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1875     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));
1876     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));
1877     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1878     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1879     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1880     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1881     break;
1882   case DM_POLYTOPE_FV_GHOST:
1883     break;
1884   default:
1885     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1886   }
1887   PetscFunctionReturn(PETSC_SUCCESS);
1888 }
1889 
1890 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1891 {
1892   PetscReal   centroid[2] = {0., 0.};
1893   PetscMPIInt rank;
1894   PetscMPIInt fillColor;
1895 
1896   PetscFunctionBegin;
1897   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1898   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1899   for (PetscInt v = 0; v < Nv; ++v) {
1900     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1901     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1902   }
1903   for (PetscInt e = 0; e < Nv; ++e) {
1904     refCoords[0] = refVertices[e * 2 + 0];
1905     refCoords[1] = refVertices[e * 2 + 1];
1906     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1907       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1908       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1909     }
1910     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1911     for (PetscInt d = 0; d < edgeDiv; ++d) {
1912       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));
1913       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1914     }
1915   }
1916   PetscFunctionReturn(PETSC_SUCCESS);
1917 }
1918 
1919 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1920 {
1921   DMPolytopeType ct;
1922 
1923   PetscFunctionBegin;
1924   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1925   switch (ct) {
1926   case DM_POLYTOPE_TRIANGLE: {
1927     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1928 
1929     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1930   } break;
1931   case DM_POLYTOPE_QUADRILATERAL: {
1932     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1933 
1934     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1935   } break;
1936   default:
1937     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1938   }
1939   PetscFunctionReturn(PETSC_SUCCESS);
1940 }
1941 
1942 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1943 {
1944   PetscDraw    draw;
1945   DM           cdm;
1946   PetscSection coordSection;
1947   Vec          coordinates;
1948   PetscReal    xyl[3], xyr[3];
1949   PetscReal   *refCoords, *edgeCoords;
1950   PetscBool    isnull, drawAffine;
1951   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1952 
1953   PetscFunctionBegin;
1954   PetscCall(DMGetCoordinateDim(dm, &dim));
1955   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1956   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1957   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1958   edgeDiv    = cDegree + 1;
1959   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1960   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1961   PetscCall(DMGetCoordinateDM(dm, &cdm));
1962   PetscCall(DMGetLocalSection(cdm, &coordSection));
1963   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1964   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1965   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1966 
1967   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1968   PetscCall(PetscDrawIsNull(draw, &isnull));
1969   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1970   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1971 
1972   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1973   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1974   PetscCall(PetscDrawClear(draw));
1975 
1976   for (c = cStart; c < cEnd; ++c) {
1977     PetscScalar       *coords = NULL;
1978     const PetscScalar *coords_arr;
1979     PetscInt           numCoords;
1980     PetscBool          isDG;
1981 
1982     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1983     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1984     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1985     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1986   }
1987   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1988   PetscCall(PetscDrawFlush(draw));
1989   PetscCall(PetscDrawPause(draw));
1990   PetscCall(PetscDrawSave(draw));
1991   PetscFunctionReturn(PETSC_SUCCESS);
1992 }
1993 
1994 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1995 {
1996   DM           odm = dm, rdm = dm, cdm;
1997   PetscFE      fe;
1998   PetscSpace   sp;
1999   PetscClassId id;
2000   PetscInt     degree;
2001   PetscBool    hoView = PETSC_TRUE;
2002 
2003   PetscFunctionBegin;
2004   PetscObjectOptionsBegin((PetscObject)dm);
2005   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2006   PetscOptionsEnd();
2007   PetscCall(PetscObjectReference((PetscObject)dm));
2008   *hdm = dm;
2009   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2010   PetscCall(DMGetCoordinateDM(dm, &cdm));
2011   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2012   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2013   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2014   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2015   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2016   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2017     DM  cdm, rcdm;
2018     Mat In;
2019     Vec cl, rcl;
2020 
2021     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2022     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
2023     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2024     PetscCall(DMGetCoordinateDM(odm, &cdm));
2025     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2026     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2027     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2028     PetscCall(DMSetCoarseDM(rcdm, cdm));
2029     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2030     PetscCall(MatMult(In, cl, rcl));
2031     PetscCall(MatDestroy(&In));
2032     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2033     PetscCall(DMDestroy(&odm));
2034     odm = rdm;
2035   }
2036   *hdm = rdm;
2037   PetscFunctionReturn(PETSC_SUCCESS);
2038 }
2039 
2040 #if defined(PETSC_HAVE_EXODUSII)
2041   #include <exodusII.h>
2042   #include <petscviewerexodusii.h>
2043 #endif
2044 
2045 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2046 {
2047   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2048   char      name[PETSC_MAX_PATH_LEN];
2049 
2050   PetscFunctionBegin;
2051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2052   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2053   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2054   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2055   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2056   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2057   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2060   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2061   if (iascii) {
2062     PetscViewerFormat format;
2063     PetscCall(PetscViewerGetFormat(viewer, &format));
2064     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2065     else PetscCall(DMPlexView_Ascii(dm, viewer));
2066   } else if (ishdf5) {
2067 #if defined(PETSC_HAVE_HDF5)
2068     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2069 #else
2070     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2071 #endif
2072   } else if (isvtk) {
2073     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2074   } else if (isdraw) {
2075     DM hdm;
2076 
2077     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2078     PetscCall(DMPlexView_Draw(hdm, viewer));
2079     PetscCall(DMDestroy(&hdm));
2080   } else if (isglvis) {
2081     PetscCall(DMPlexView_GLVis(dm, viewer));
2082 #if defined(PETSC_HAVE_EXODUSII)
2083   } else if (isexodus) {
2084     /*
2085       exodusII requires that all sets be part of exactly one cell set.
2086       If the dm does not have a "Cell Sets" label defined, we create one
2087       with ID 1, containing all cells.
2088       Note that if the Cell Sets label is defined but does not cover all cells,
2089       we may still have a problem. This should probably be checked here or in the viewer;
2090     */
2091     PetscInt numCS;
2092     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2093     if (!numCS) {
2094       PetscInt cStart, cEnd, c;
2095       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2096       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2097       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2098     }
2099     PetscCall(DMView_PlexExodusII(dm, viewer));
2100 #endif
2101 #if defined(PETSC_HAVE_CGNS)
2102   } else if (iscgns) {
2103     PetscCall(DMView_PlexCGNS(dm, viewer));
2104 #endif
2105   } else if (ispython) {
2106     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2107   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2108   /* Optionally view the partition */
2109   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2110   if (flg) {
2111     Vec ranks;
2112     PetscCall(DMPlexCreateRankField(dm, &ranks));
2113     PetscCall(VecView(ranks, viewer));
2114     PetscCall(VecDestroy(&ranks));
2115   }
2116   /* Optionally view a label */
2117   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2118   if (flg) {
2119     DMLabel label;
2120     Vec     val;
2121 
2122     PetscCall(DMGetLabel(dm, name, &label));
2123     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2124     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2125     PetscCall(VecView(val, viewer));
2126     PetscCall(VecDestroy(&val));
2127   }
2128   PetscFunctionReturn(PETSC_SUCCESS);
2129 }
2130 
2131 /*@
2132   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2133 
2134   Collective
2135 
2136   Input Parameters:
2137 + dm     - The `DM` whose topology is to be saved
2138 - viewer - The `PetscViewer` to save it in
2139 
2140   Level: advanced
2141 
2142 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2143 @*/
2144 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2145 {
2146   PetscBool ishdf5;
2147 
2148   PetscFunctionBegin;
2149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2150   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2151   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2152   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2153   if (ishdf5) {
2154 #if defined(PETSC_HAVE_HDF5)
2155     PetscViewerFormat format;
2156     PetscCall(PetscViewerGetFormat(viewer, &format));
2157     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2158       IS globalPointNumbering;
2159 
2160       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2161       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2162       PetscCall(ISDestroy(&globalPointNumbering));
2163     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2164 #else
2165     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2166 #endif
2167   }
2168   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2169   PetscFunctionReturn(PETSC_SUCCESS);
2170 }
2171 
2172 /*@
2173   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2174 
2175   Collective
2176 
2177   Input Parameters:
2178 + dm     - The `DM` whose coordinates are to be saved
2179 - viewer - The `PetscViewer` for saving
2180 
2181   Level: advanced
2182 
2183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2184 @*/
2185 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2186 {
2187   PetscBool ishdf5;
2188 
2189   PetscFunctionBegin;
2190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2191   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2192   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2193   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2194   if (ishdf5) {
2195 #if defined(PETSC_HAVE_HDF5)
2196     PetscViewerFormat format;
2197     PetscCall(PetscViewerGetFormat(viewer, &format));
2198     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2199       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2200     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2201 #else
2202     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2203 #endif
2204   }
2205   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2206   PetscFunctionReturn(PETSC_SUCCESS);
2207 }
2208 
2209 /*@
2210   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2211 
2212   Collective
2213 
2214   Input Parameters:
2215 + dm     - The `DM` whose labels are to be saved
2216 - viewer - The `PetscViewer` for saving
2217 
2218   Level: advanced
2219 
2220 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2221 @*/
2222 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2223 {
2224   PetscBool ishdf5;
2225 
2226   PetscFunctionBegin;
2227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2228   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2229   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2230   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2231   if (ishdf5) {
2232 #if defined(PETSC_HAVE_HDF5)
2233     IS                globalPointNumbering;
2234     PetscViewerFormat format;
2235 
2236     PetscCall(PetscViewerGetFormat(viewer, &format));
2237     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2238       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2239       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2240       PetscCall(ISDestroy(&globalPointNumbering));
2241     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2242 #else
2243     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2244 #endif
2245   }
2246   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2247   PetscFunctionReturn(PETSC_SUCCESS);
2248 }
2249 
2250 /*@
2251   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2252 
2253   Collective
2254 
2255   Input Parameters:
2256 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2257 . viewer    - The `PetscViewer` for saving
2258 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2259 
2260   Level: advanced
2261 
2262   Notes:
2263   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.
2264 
2265   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.
2266 
2267 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2268 @*/
2269 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2270 {
2271   PetscBool ishdf5;
2272 
2273   PetscFunctionBegin;
2274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2275   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2276   if (!sectiondm) sectiondm = dm;
2277   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2278   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2279   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2280   if (ishdf5) {
2281 #if defined(PETSC_HAVE_HDF5)
2282     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2283 #else
2284     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2285 #endif
2286   }
2287   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2288   PetscFunctionReturn(PETSC_SUCCESS);
2289 }
2290 
2291 /*@
2292   DMPlexGlobalVectorView - Saves a global vector
2293 
2294   Collective
2295 
2296   Input Parameters:
2297 + dm        - The `DM` that represents the topology
2298 . viewer    - The `PetscViewer` to save data with
2299 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2300 - vec       - The global vector to be saved
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   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.
2306 
2307   Calling sequence:
2308 .vb
2309        DMCreate(PETSC_COMM_WORLD, &dm);
2310        DMSetType(dm, DMPLEX);
2311        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2312        DMClone(dm, &sectiondm);
2313        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2314        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2315        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2316        PetscSectionSetChart(section, pStart, pEnd);
2317        PetscSectionSetUp(section);
2318        DMSetLocalSection(sectiondm, section);
2319        PetscSectionDestroy(&section);
2320        DMGetGlobalVector(sectiondm, &vec);
2321        PetscObjectSetName((PetscObject)vec, "vec_name");
2322        DMPlexTopologyView(dm, viewer);
2323        DMPlexSectionView(dm, viewer, sectiondm);
2324        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2325        DMRestoreGlobalVector(sectiondm, &vec);
2326        DMDestroy(&sectiondm);
2327        DMDestroy(&dm);
2328 .ve
2329 
2330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2331 @*/
2332 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2333 {
2334   PetscBool ishdf5;
2335 
2336   PetscFunctionBegin;
2337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2338   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2339   if (!sectiondm) sectiondm = dm;
2340   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2341   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2342   /* Check consistency */
2343   {
2344     PetscSection section;
2345     PetscBool    includesConstraints;
2346     PetscInt     m, m1;
2347 
2348     PetscCall(VecGetLocalSize(vec, &m1));
2349     PetscCall(DMGetGlobalSection(sectiondm, &section));
2350     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2351     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2352     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2353     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2354   }
2355   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2356   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2357   if (ishdf5) {
2358 #if defined(PETSC_HAVE_HDF5)
2359     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2360 #else
2361     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2362 #endif
2363   }
2364   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2365   PetscFunctionReturn(PETSC_SUCCESS);
2366 }
2367 
2368 /*@
2369   DMPlexLocalVectorView - Saves a local vector
2370 
2371   Collective
2372 
2373   Input Parameters:
2374 + dm        - The `DM` that represents the topology
2375 . viewer    - The `PetscViewer` to save data with
2376 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2377 - vec       - The local vector to be saved
2378 
2379   Level: advanced
2380 
2381   Note:
2382   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.
2383 
2384   Calling sequence:
2385 .vb
2386        DMCreate(PETSC_COMM_WORLD, &dm);
2387        DMSetType(dm, DMPLEX);
2388        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2389        DMClone(dm, &sectiondm);
2390        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2391        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2392        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2393        PetscSectionSetChart(section, pStart, pEnd);
2394        PetscSectionSetUp(section);
2395        DMSetLocalSection(sectiondm, section);
2396        DMGetLocalVector(sectiondm, &vec);
2397        PetscObjectSetName((PetscObject)vec, "vec_name");
2398        DMPlexTopologyView(dm, viewer);
2399        DMPlexSectionView(dm, viewer, sectiondm);
2400        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2401        DMRestoreLocalVector(sectiondm, &vec);
2402        DMDestroy(&sectiondm);
2403        DMDestroy(&dm);
2404 .ve
2405 
2406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2407 @*/
2408 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2409 {
2410   PetscBool ishdf5;
2411 
2412   PetscFunctionBegin;
2413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2414   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2415   if (!sectiondm) sectiondm = dm;
2416   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2417   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2418   /* Check consistency */
2419   {
2420     PetscSection section;
2421     PetscBool    includesConstraints;
2422     PetscInt     m, m1;
2423 
2424     PetscCall(VecGetLocalSize(vec, &m1));
2425     PetscCall(DMGetLocalSection(sectiondm, &section));
2426     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2427     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2428     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2429     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2430   }
2431   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2432   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2433   if (ishdf5) {
2434 #if defined(PETSC_HAVE_HDF5)
2435     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2436 #else
2437     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2438 #endif
2439   }
2440   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2441   PetscFunctionReturn(PETSC_SUCCESS);
2442 }
2443 
2444 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2445 {
2446   PetscBool ishdf5;
2447 
2448   PetscFunctionBegin;
2449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2450   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2452   if (ishdf5) {
2453 #if defined(PETSC_HAVE_HDF5)
2454     PetscViewerFormat format;
2455     PetscCall(PetscViewerGetFormat(viewer, &format));
2456     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2457       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2458     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2459       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2460     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2461     PetscFunctionReturn(PETSC_SUCCESS);
2462 #else
2463     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2464 #endif
2465   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2466 }
2467 
2468 /*@
2469   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2470 
2471   Collective
2472 
2473   Input Parameters:
2474 + dm     - The `DM` into which the topology is loaded
2475 - viewer - The `PetscViewer` for the saved topology
2476 
2477   Output Parameter:
2478 . 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;
2479   `NULL` if unneeded
2480 
2481   Level: advanced
2482 
2483 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2484           `PetscViewer`, `PetscSF`
2485 @*/
2486 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2487 {
2488   PetscBool ishdf5;
2489 
2490   PetscFunctionBegin;
2491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2492   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2493   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2494   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2495   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2496   if (ishdf5) {
2497 #if defined(PETSC_HAVE_HDF5)
2498     PetscViewerFormat format;
2499     PetscCall(PetscViewerGetFormat(viewer, &format));
2500     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2501       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2502     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2503 #else
2504     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2505 #endif
2506   }
2507   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2508   PetscFunctionReturn(PETSC_SUCCESS);
2509 }
2510 
2511 /*@
2512   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2513 
2514   Collective
2515 
2516   Input Parameters:
2517 + dm                   - The `DM` into which the coordinates are loaded
2518 . viewer               - The `PetscViewer` for the saved coordinates
2519 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2520 
2521   Level: advanced
2522 
2523 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2524           `PetscSF`, `PetscViewer`
2525 @*/
2526 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2527 {
2528   PetscBool ishdf5;
2529 
2530   PetscFunctionBegin;
2531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2532   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2533   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2535   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2536   if (ishdf5) {
2537 #if defined(PETSC_HAVE_HDF5)
2538     PetscViewerFormat format;
2539     PetscCall(PetscViewerGetFormat(viewer, &format));
2540     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2541       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2542     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2543 #else
2544     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2545 #endif
2546   }
2547   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2548   PetscFunctionReturn(PETSC_SUCCESS);
2549 }
2550 
2551 /*@
2552   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2553 
2554   Collective
2555 
2556   Input Parameters:
2557 + dm                   - The `DM` into which the labels are loaded
2558 . viewer               - The `PetscViewer` for the saved labels
2559 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2560 
2561   Level: advanced
2562 
2563   Note:
2564   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2565 
2566 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2567           `PetscSF`, `PetscViewer`
2568 @*/
2569 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2570 {
2571   PetscBool ishdf5;
2572 
2573   PetscFunctionBegin;
2574   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2575   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2576   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2578   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2579   if (ishdf5) {
2580 #if defined(PETSC_HAVE_HDF5)
2581     PetscViewerFormat format;
2582 
2583     PetscCall(PetscViewerGetFormat(viewer, &format));
2584     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2585       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2586     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2587 #else
2588     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2589 #endif
2590   }
2591   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2592   PetscFunctionReturn(PETSC_SUCCESS);
2593 }
2594 
2595 /*@
2596   DMPlexSectionLoad - Loads section into a `DMPLEX`
2597 
2598   Collective
2599 
2600   Input Parameters:
2601 + dm                   - The `DM` that represents the topology
2602 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2603 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2604 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2605 
2606   Output Parameters:
2607 + 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)
2608 - 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)
2609 
2610   Level: advanced
2611 
2612   Notes:
2613   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.
2614 
2615   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.
2616 
2617   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.
2618 
2619   Example using 2 processes:
2620 .vb
2621   NX (number of points on dm): 4
2622   sectionA                   : the on-disk section
2623   vecA                       : a vector associated with sectionA
2624   sectionB                   : sectiondm's local section constructed in this function
2625   vecB (local)               : a vector associated with sectiondm's local section
2626   vecB (global)              : a vector associated with sectiondm's global section
2627 
2628                                      rank 0    rank 1
2629   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2630   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2631   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2632   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2633   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2634   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2635   sectionB->atlasDof             :     1 0 1 | 1 3
2636   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2637   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2638   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2639 .ve
2640   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2641 
2642 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2643 @*/
2644 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2645 {
2646   PetscBool ishdf5;
2647 
2648   PetscFunctionBegin;
2649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2650   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2651   if (!sectiondm) sectiondm = dm;
2652   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2653   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2654   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2655   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2657   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2658   if (ishdf5) {
2659 #if defined(PETSC_HAVE_HDF5)
2660     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2661 #else
2662     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2663 #endif
2664   }
2665   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2666   PetscFunctionReturn(PETSC_SUCCESS);
2667 }
2668 
2669 /*@
2670   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2671 
2672   Collective
2673 
2674   Input Parameters:
2675 + dm        - The `DM` that represents the topology
2676 . viewer    - The `PetscViewer` that represents the on-disk vector data
2677 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2678 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2679 - vec       - The global vector to set values of
2680 
2681   Level: advanced
2682 
2683   Notes:
2684   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.
2685 
2686   Calling sequence:
2687 .vb
2688        DMCreate(PETSC_COMM_WORLD, &dm);
2689        DMSetType(dm, DMPLEX);
2690        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2691        DMPlexTopologyLoad(dm, viewer, &sfX);
2692        DMClone(dm, &sectiondm);
2693        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2694        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2695        DMGetGlobalVector(sectiondm, &vec);
2696        PetscObjectSetName((PetscObject)vec, "vec_name");
2697        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2698        DMRestoreGlobalVector(sectiondm, &vec);
2699        PetscSFDestroy(&gsf);
2700        PetscSFDestroy(&sfX);
2701        DMDestroy(&sectiondm);
2702        DMDestroy(&dm);
2703 .ve
2704 
2705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2706           `PetscSF`, `PetscViewer`
2707 @*/
2708 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2709 {
2710   PetscBool ishdf5;
2711 
2712   PetscFunctionBegin;
2713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2714   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2715   if (!sectiondm) sectiondm = dm;
2716   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2717   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2718   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2719   /* Check consistency */
2720   {
2721     PetscSection section;
2722     PetscBool    includesConstraints;
2723     PetscInt     m, m1;
2724 
2725     PetscCall(VecGetLocalSize(vec, &m1));
2726     PetscCall(DMGetGlobalSection(sectiondm, &section));
2727     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2728     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2729     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2730     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2731   }
2732   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2733   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2734   if (ishdf5) {
2735 #if defined(PETSC_HAVE_HDF5)
2736     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2737 #else
2738     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2739 #endif
2740   }
2741   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2742   PetscFunctionReturn(PETSC_SUCCESS);
2743 }
2744 
2745 /*@
2746   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2747 
2748   Collective
2749 
2750   Input Parameters:
2751 + dm        - The `DM` that represents the topology
2752 . viewer    - The `PetscViewer` that represents the on-disk vector data
2753 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2754 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2755 - vec       - The local vector to set values of
2756 
2757   Level: advanced
2758 
2759   Notes:
2760   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.
2761 
2762   Calling sequence:
2763 .vb
2764        DMCreate(PETSC_COMM_WORLD, &dm);
2765        DMSetType(dm, DMPLEX);
2766        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2767        DMPlexTopologyLoad(dm, viewer, &sfX);
2768        DMClone(dm, &sectiondm);
2769        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2770        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2771        DMGetLocalVector(sectiondm, &vec);
2772        PetscObjectSetName((PetscObject)vec, "vec_name");
2773        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2774        DMRestoreLocalVector(sectiondm, &vec);
2775        PetscSFDestroy(&lsf);
2776        PetscSFDestroy(&sfX);
2777        DMDestroy(&sectiondm);
2778        DMDestroy(&dm);
2779 .ve
2780 
2781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2782           `PetscSF`, `PetscViewer`
2783 @*/
2784 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2785 {
2786   PetscBool ishdf5;
2787 
2788   PetscFunctionBegin;
2789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2790   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2791   if (!sectiondm) sectiondm = dm;
2792   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2793   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2794   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2795   /* Check consistency */
2796   {
2797     PetscSection section;
2798     PetscBool    includesConstraints;
2799     PetscInt     m, m1;
2800 
2801     PetscCall(VecGetLocalSize(vec, &m1));
2802     PetscCall(DMGetLocalSection(sectiondm, &section));
2803     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2804     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2805     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2806     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2807   }
2808   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2809   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2810   if (ishdf5) {
2811 #if defined(PETSC_HAVE_HDF5)
2812     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2813 #else
2814     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2815 #endif
2816   }
2817   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2818   PetscFunctionReturn(PETSC_SUCCESS);
2819 }
2820 
2821 PetscErrorCode DMDestroy_Plex(DM dm)
2822 {
2823   DM_Plex *mesh = (DM_Plex *)dm->data;
2824 
2825   PetscFunctionBegin;
2826   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2827   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2828   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2829   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2830   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2837   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2838   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2839   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2840   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2841   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2842   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2843   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2844   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2845   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2846   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2847   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2848   PetscCall(PetscFree(mesh->cones));
2849   PetscCall(PetscFree(mesh->coneOrientations));
2850   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2851   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2852   PetscCall(PetscFree(mesh->supports));
2853   PetscCall(PetscFree(mesh->cellTypes));
2854   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2855   PetscCall(PetscFree(mesh->tetgenOpts));
2856   PetscCall(PetscFree(mesh->triangleOpts));
2857   PetscCall(PetscFree(mesh->transformType));
2858   PetscCall(PetscFree(mesh->distributionName));
2859   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2860   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2861   PetscCall(ISDestroy(&mesh->subpointIS));
2862   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2863   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2864   if (mesh->periodic.face_sfs) {
2865     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2866     PetscCall(PetscFree(mesh->periodic.face_sfs));
2867   }
2868   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2869   if (mesh->periodic.periodic_points) {
2870     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2871     PetscCall(PetscFree(mesh->periodic.periodic_points));
2872   }
2873   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2874   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2875   PetscCall(ISDestroy(&mesh->anchorIS));
2876   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2877   PetscCall(PetscFree(mesh->parents));
2878   PetscCall(PetscFree(mesh->childIDs));
2879   PetscCall(PetscSectionDestroy(&mesh->childSection));
2880   PetscCall(PetscFree(mesh->children));
2881   PetscCall(DMDestroy(&mesh->referenceTree));
2882   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2883   PetscCall(PetscFree(mesh->neighbors));
2884   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2885   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2886   PetscCall(DMPlexTransformDestroy(&mesh->transform));
2887   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2888   PetscCall(PetscFree(mesh));
2889   PetscFunctionReturn(PETSC_SUCCESS);
2890 }
2891 
2892 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2893 {
2894   PetscSection           sectionGlobal, sectionLocal;
2895   PetscInt               bs = -1, mbs;
2896   PetscInt               localSize, localStart = 0;
2897   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2898   MatType                mtype;
2899   ISLocalToGlobalMapping ltog;
2900 
2901   PetscFunctionBegin;
2902   PetscCall(MatInitializePackage());
2903   mtype = dm->mattype;
2904   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2905   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2906   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2907   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2908   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2909   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2910   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2911   PetscCall(MatSetType(*J, mtype));
2912   PetscCall(MatSetFromOptions(*J));
2913   PetscCall(MatGetBlockSize(*J, &mbs));
2914   if (mbs > 1) bs = mbs;
2915   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2916   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2917   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2918   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2919   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2920   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2921   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2922   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2923   if (!isShell) {
2924     // There are three states with pblocks, since block starts can have no dofs:
2925     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2926     // TRUE)    Block Start: The first entry in a block has been added
2927     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2928     PetscBT         blst;
2929     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2930     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2931     const PetscInt *perm       = NULL;
2932     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2933     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2934 
2935     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2936     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2937     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2938 
2939     PetscCall(PetscCalloc1(localSize, &pblocks));
2940     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2941     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2942     // We need to process in the permuted order to get block sizes right
2943     for (PetscInt point = pStart; point < pEnd; ++point) {
2944       const PetscInt p = perm ? perm[point] : point;
2945 
2946       switch (dm->blocking_type) {
2947       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2948         PetscInt bdof, offset;
2949 
2950         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2951         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2952         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2953         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2954         if (dof > 0) {
2955           // State change
2956           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2957           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2958 
2959           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2960           // Signal block concatenation
2961           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2962         }
2963         dof  = dof < 0 ? -(dof + 1) : dof;
2964         bdof = cdof && (dof - cdof) ? 1 : dof;
2965         if (dof) {
2966           if (bs < 0) {
2967             bs = bdof;
2968           } else if (bs != bdof) {
2969             bs = 1;
2970           }
2971         }
2972       } break;
2973       case DM_BLOCKING_FIELD_NODE: {
2974         for (PetscInt field = 0; field < num_fields; field++) {
2975           PetscInt num_comp, bdof, offset;
2976           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2977           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2978           if (dof < 0) continue;
2979           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2980           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2981           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);
2982           PetscInt num_nodes = dof / num_comp;
2983           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2984           // Handle possibly constant block size (unlikely)
2985           bdof = cdof && (dof - cdof) ? 1 : dof;
2986           if (dof) {
2987             if (bs < 0) {
2988               bs = bdof;
2989             } else if (bs != bdof) {
2990               bs = 1;
2991             }
2992           }
2993         }
2994       } break;
2995       }
2996     }
2997     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2998     /* Must have same blocksize on all procs (some might have no points) */
2999     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
3000     bsLocal[1] = bs;
3001     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
3002     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
3003     else bs = bsMinMax[0];
3004     bs = PetscMax(1, bs);
3005     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
3006     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
3007       PetscCall(MatSetBlockSize(*J, bs));
3008       PetscCall(MatSetUp(*J));
3009     } else {
3010       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3011       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3012       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3013     }
3014     if (pblocks) { // Consolidate blocks
3015       PetscInt nblocks = 0;
3016       pblocks[0]       = PetscAbs(pblocks[0]);
3017       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3018         if (pblocks[i] == 0) continue;
3019         // Negative block size indicates the blocks should be concatenated
3020         if (pblocks[i] < 0) {
3021           pblocks[i] = -pblocks[i];
3022           pblocks[nblocks - 1] += pblocks[i];
3023         } else {
3024           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3025         }
3026         for (PetscInt j = 1; j < pblocks[i]; j++)
3027           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);
3028       }
3029       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3030     }
3031     PetscCall(PetscFree(pblocks));
3032   }
3033   PetscCall(MatSetDM(*J, dm));
3034   PetscFunctionReturn(PETSC_SUCCESS);
3035 }
3036 
3037 /*@
3038   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3039 
3040   Not Collective
3041 
3042   Input Parameter:
3043 . dm - The `DMPLEX`
3044 
3045   Output Parameter:
3046 . subsection - The subdomain section
3047 
3048   Level: developer
3049 
3050 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3051 @*/
3052 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3053 {
3054   DM_Plex *mesh = (DM_Plex *)dm->data;
3055 
3056   PetscFunctionBegin;
3057   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3058   if (!mesh->subdomainSection) {
3059     PetscSection section;
3060     PetscSF      sf;
3061 
3062     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3063     PetscCall(DMGetLocalSection(dm, &section));
3064     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3065     PetscCall(PetscSFDestroy(&sf));
3066   }
3067   *subsection = mesh->subdomainSection;
3068   PetscFunctionReturn(PETSC_SUCCESS);
3069 }
3070 
3071 /*@
3072   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3073 
3074   Not Collective
3075 
3076   Input Parameter:
3077 . dm - The `DMPLEX`
3078 
3079   Output Parameters:
3080 + pStart - The first mesh point
3081 - pEnd   - The upper bound for mesh points
3082 
3083   Level: beginner
3084 
3085 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3086 @*/
3087 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3088 {
3089   DM_Plex *mesh = (DM_Plex *)dm->data;
3090 
3091   PetscFunctionBegin;
3092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3093   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3094   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3095   PetscFunctionReturn(PETSC_SUCCESS);
3096 }
3097 
3098 /*@
3099   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3100 
3101   Not Collective
3102 
3103   Input Parameters:
3104 + dm     - The `DMPLEX`
3105 . pStart - The first mesh point
3106 - pEnd   - The upper bound for mesh points
3107 
3108   Level: beginner
3109 
3110 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3111 @*/
3112 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3113 {
3114   DM_Plex *mesh = (DM_Plex *)dm->data;
3115 
3116   PetscFunctionBegin;
3117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3118   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3119   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3120   PetscCall(PetscFree(mesh->cellTypes));
3121   PetscFunctionReturn(PETSC_SUCCESS);
3122 }
3123 
3124 /*@
3125   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3126 
3127   Not Collective
3128 
3129   Input Parameters:
3130 + dm - The `DMPLEX`
3131 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3132 
3133   Output Parameter:
3134 . size - The cone size for point `p`
3135 
3136   Level: beginner
3137 
3138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3139 @*/
3140 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3141 {
3142   DM_Plex *mesh = (DM_Plex *)dm->data;
3143 
3144   PetscFunctionBegin;
3145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3146   PetscAssertPointer(size, 3);
3147   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3148   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3149   PetscFunctionReturn(PETSC_SUCCESS);
3150 }
3151 
3152 /*@
3153   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3154 
3155   Not Collective
3156 
3157   Input Parameters:
3158 + dm   - The `DMPLEX`
3159 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3160 - size - The cone size for point `p`
3161 
3162   Level: beginner
3163 
3164   Note:
3165   This should be called after `DMPlexSetChart()`.
3166 
3167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3168 @*/
3169 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3170 {
3171   DM_Plex *mesh = (DM_Plex *)dm->data;
3172 
3173   PetscFunctionBegin;
3174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3175   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3176   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3177   PetscFunctionReturn(PETSC_SUCCESS);
3178 }
3179 
3180 /*@C
3181   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3182 
3183   Not Collective
3184 
3185   Input Parameters:
3186 + dm - The `DMPLEX`
3187 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3188 
3189   Output Parameter:
3190 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3191 
3192   Level: beginner
3193 
3194   Fortran Notes:
3195   `cone` must be declared with
3196 .vb
3197   PetscInt, pointer :: cone(:)
3198 .ve
3199 
3200   You must call `DMPlexRestoreCone()` after you finish using the array.
3201   `DMPlexRestoreCone()` is not needed/available in C.
3202 
3203 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3204 @*/
3205 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3206 {
3207   DM_Plex *mesh = (DM_Plex *)dm->data;
3208   PetscInt off;
3209 
3210   PetscFunctionBegin;
3211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3212   PetscAssertPointer(cone, 3);
3213   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3214   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3215   PetscFunctionReturn(PETSC_SUCCESS);
3216 }
3217 
3218 /*@
3219   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3220 
3221   Not Collective
3222 
3223   Input Parameters:
3224 + dm - The `DMPLEX`
3225 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3226 
3227   Output Parameters:
3228 + pConesSection - `PetscSection` describing the layout of `pCones`
3229 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3230 
3231   Level: intermediate
3232 
3233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3234 @*/
3235 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3236 {
3237   PetscSection cs, newcs;
3238   PetscInt    *cones;
3239   PetscInt    *newarr = NULL;
3240   PetscInt     n;
3241 
3242   PetscFunctionBegin;
3243   PetscCall(DMPlexGetCones(dm, &cones));
3244   PetscCall(DMPlexGetConeSection(dm, &cs));
3245   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3246   if (pConesSection) *pConesSection = newcs;
3247   if (pCones) {
3248     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3249     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3250   }
3251   PetscFunctionReturn(PETSC_SUCCESS);
3252 }
3253 
3254 /*@
3255   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3256 
3257   Not Collective
3258 
3259   Input Parameters:
3260 + dm     - The `DMPLEX`
3261 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3262 
3263   Output Parameter:
3264 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3265 
3266   Level: advanced
3267 
3268   Notes:
3269   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3270 
3271   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3272 
3273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3274           `DMPlexGetDepth()`, `IS`
3275 @*/
3276 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3277 {
3278   IS      *expandedPointsAll;
3279   PetscInt depth;
3280 
3281   PetscFunctionBegin;
3282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3283   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3284   PetscAssertPointer(expandedPoints, 3);
3285   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3286   *expandedPoints = expandedPointsAll[0];
3287   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3288   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3289   PetscFunctionReturn(PETSC_SUCCESS);
3290 }
3291 
3292 /*@
3293   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3294   (DAG points of depth 0, i.e., without cones).
3295 
3296   Not Collective
3297 
3298   Input Parameters:
3299 + dm     - The `DMPLEX`
3300 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3301 
3302   Output Parameters:
3303 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3304 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3305 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3306 
3307   Level: advanced
3308 
3309   Notes:
3310   Like `DMPlexGetConeTuple()` but recursive.
3311 
3312   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.
3313   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3314 
3315   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\:
3316   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3317   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3318 
3319 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3320           `DMPlexGetDepth()`, `PetscSection`, `IS`
3321 @*/
3322 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3323 {
3324   const PetscInt *arr0 = NULL, *cone = NULL;
3325   PetscInt       *arr = NULL, *newarr = NULL;
3326   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3327   IS             *expandedPoints_;
3328   PetscSection   *sections_;
3329 
3330   PetscFunctionBegin;
3331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3332   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3333   if (depth) PetscAssertPointer(depth, 3);
3334   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3335   if (sections) PetscAssertPointer(sections, 5);
3336   PetscCall(ISGetLocalSize(points, &n));
3337   PetscCall(ISGetIndices(points, &arr0));
3338   PetscCall(DMPlexGetDepth(dm, &depth_));
3339   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3340   PetscCall(PetscCalloc1(depth_, &sections_));
3341   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3342   for (d = depth_ - 1; d >= 0; d--) {
3343     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3344     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3345     for (i = 0; i < n; i++) {
3346       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3347       if (arr[i] >= start && arr[i] < end) {
3348         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3349         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3350       } else {
3351         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3352       }
3353     }
3354     PetscCall(PetscSectionSetUp(sections_[d]));
3355     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3356     PetscCall(PetscMalloc1(newn, &newarr));
3357     for (i = 0; i < n; i++) {
3358       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3359       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3360       if (cn > 1) {
3361         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3362         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3363       } else {
3364         newarr[co] = arr[i];
3365       }
3366     }
3367     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3368     arr = newarr;
3369     n   = newn;
3370   }
3371   PetscCall(ISRestoreIndices(points, &arr0));
3372   *depth = depth_;
3373   if (expandedPoints) *expandedPoints = expandedPoints_;
3374   else {
3375     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3376     PetscCall(PetscFree(expandedPoints_));
3377   }
3378   if (sections) *sections = sections_;
3379   else {
3380     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3381     PetscCall(PetscFree(sections_));
3382   }
3383   PetscFunctionReturn(PETSC_SUCCESS);
3384 }
3385 
3386 /*@
3387   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3388 
3389   Not Collective
3390 
3391   Input Parameters:
3392 + dm     - The `DMPLEX`
3393 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3394 
3395   Output Parameters:
3396 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3397 . expandedPoints - (optional) An array of recursively expanded cones
3398 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3399 
3400   Level: advanced
3401 
3402   Note:
3403   See `DMPlexGetConeRecursive()`
3404 
3405 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3406           `DMPlexGetDepth()`, `IS`, `PetscSection`
3407 @*/
3408 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3409 {
3410   PetscInt d, depth_;
3411 
3412   PetscFunctionBegin;
3413   PetscCall(DMPlexGetDepth(dm, &depth_));
3414   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3415   if (depth) *depth = 0;
3416   if (expandedPoints) {
3417     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3418     PetscCall(PetscFree(*expandedPoints));
3419   }
3420   if (sections) {
3421     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3422     PetscCall(PetscFree(*sections));
3423   }
3424   PetscFunctionReturn(PETSC_SUCCESS);
3425 }
3426 
3427 /*@
3428   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
3429 
3430   Not Collective
3431 
3432   Input Parameters:
3433 + dm   - The `DMPLEX`
3434 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3435 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3436 
3437   Level: beginner
3438 
3439   Note:
3440   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3441 
3442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3443 @*/
3444 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3445 {
3446   DM_Plex *mesh = (DM_Plex *)dm->data;
3447   PetscInt dof, off, c;
3448 
3449   PetscFunctionBegin;
3450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3451   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3452   if (dof) PetscAssertPointer(cone, 3);
3453   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3454   if (PetscDefined(USE_DEBUG)) {
3455     PetscInt pStart, pEnd;
3456     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3457     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);
3458     for (c = 0; c < dof; ++c) {
3459       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);
3460       mesh->cones[off + c] = cone[c];
3461     }
3462   } else {
3463     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3464   }
3465   PetscFunctionReturn(PETSC_SUCCESS);
3466 }
3467 
3468 /*@C
3469   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3470 
3471   Not Collective
3472 
3473   Input Parameters:
3474 + dm - The `DMPLEX`
3475 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3476 
3477   Output Parameter:
3478 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3479                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3480 
3481   Level: beginner
3482 
3483   Note:
3484   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3485   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3486   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3487   with the identity.
3488 
3489   Fortran Notes:
3490   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3491   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3492 
3493 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3494           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3495 @*/
3496 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3497 {
3498   DM_Plex *mesh = (DM_Plex *)dm->data;
3499   PetscInt off;
3500 
3501   PetscFunctionBegin;
3502   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3503   if (PetscDefined(USE_DEBUG)) {
3504     PetscInt dof;
3505     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3506     if (dof) PetscAssertPointer(coneOrientation, 3);
3507   }
3508   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3509 
3510   *coneOrientation = &mesh->coneOrientations[off];
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /*@
3515   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3516 
3517   Not Collective
3518 
3519   Input Parameters:
3520 + dm              - The `DMPLEX`
3521 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3522 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3523 
3524   Level: beginner
3525 
3526   Notes:
3527   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3528 
3529   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3530 
3531 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3532 @*/
3533 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3534 {
3535   DM_Plex *mesh = (DM_Plex *)dm->data;
3536   PetscInt pStart, pEnd;
3537   PetscInt dof, off, c;
3538 
3539   PetscFunctionBegin;
3540   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3541   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3542   if (dof) PetscAssertPointer(coneOrientation, 3);
3543   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3544   if (PetscDefined(USE_DEBUG)) {
3545     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3546     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);
3547     for (c = 0; c < dof; ++c) {
3548       PetscInt cdof, o = coneOrientation[c];
3549 
3550       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3551       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);
3552       mesh->coneOrientations[off + c] = o;
3553     }
3554   } else {
3555     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3556   }
3557   PetscFunctionReturn(PETSC_SUCCESS);
3558 }
3559 
3560 /*@
3561   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3562 
3563   Not Collective
3564 
3565   Input Parameters:
3566 + dm        - The `DMPLEX`
3567 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3568 . conePos   - The local index in the cone where the point should be put
3569 - conePoint - The mesh point to insert
3570 
3571   Level: beginner
3572 
3573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3574 @*/
3575 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3576 {
3577   DM_Plex *mesh = (DM_Plex *)dm->data;
3578   PetscInt pStart, pEnd;
3579   PetscInt dof, off;
3580 
3581   PetscFunctionBegin;
3582   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3583   if (PetscDefined(USE_DEBUG)) {
3584     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3585     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);
3586     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);
3587     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3588     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);
3589   }
3590   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3591   mesh->cones[off + conePos] = conePoint;
3592   PetscFunctionReturn(PETSC_SUCCESS);
3593 }
3594 
3595 /*@
3596   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3597 
3598   Not Collective
3599 
3600   Input Parameters:
3601 + dm              - The `DMPLEX`
3602 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3603 . conePos         - The local index in the cone where the point should be put
3604 - coneOrientation - The point orientation to insert
3605 
3606   Level: beginner
3607 
3608   Note:
3609   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3610 
3611 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3612 @*/
3613 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3614 {
3615   DM_Plex *mesh = (DM_Plex *)dm->data;
3616   PetscInt pStart, pEnd;
3617   PetscInt dof, off;
3618 
3619   PetscFunctionBegin;
3620   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3621   if (PetscDefined(USE_DEBUG)) {
3622     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3623     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);
3624     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3625     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);
3626   }
3627   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3628   mesh->coneOrientations[off + conePos] = coneOrientation;
3629   PetscFunctionReturn(PETSC_SUCCESS);
3630 }
3631 
3632 /*@C
3633   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3634 
3635   Not collective
3636 
3637   Input Parameters:
3638 + dm - The DMPlex
3639 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3640 
3641   Output Parameters:
3642 + cone - An array of points which are on the in-edges for point `p`
3643 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3644          integer giving the prescription for cone traversal.
3645 
3646   Level: beginner
3647 
3648   Notes:
3649   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3650   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3651   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3652   with the identity.
3653 
3654   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3655 
3656   Fortran Notes:
3657   `cone` and `ornt` must be declared with
3658 .vb
3659   PetscInt, pointer :: cone(:)
3660   PetscInt, pointer :: ornt(:)
3661 .ve
3662 
3663 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3664 @*/
3665 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3666 {
3667   DM_Plex *mesh = (DM_Plex *)dm->data;
3668 
3669   PetscFunctionBegin;
3670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3671   if (mesh->tr) {
3672     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3673   } else {
3674     PetscInt off;
3675     if (PetscDefined(USE_DEBUG)) {
3676       PetscInt dof;
3677       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3678       if (dof) {
3679         if (cone) PetscAssertPointer(cone, 3);
3680         if (ornt) PetscAssertPointer(ornt, 4);
3681       }
3682     }
3683     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3684     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3685     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3686   }
3687   PetscFunctionReturn(PETSC_SUCCESS);
3688 }
3689 
3690 /*@C
3691   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3692 
3693   Not Collective
3694 
3695   Input Parameters:
3696 + dm   - The DMPlex
3697 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3698 . cone - An array of points which are on the in-edges for point p
3699 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3700          integer giving the prescription for cone traversal.
3701 
3702   Level: beginner
3703 
3704 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3705 @*/
3706 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3707 {
3708   DM_Plex *mesh = (DM_Plex *)dm->data;
3709 
3710   PetscFunctionBegin;
3711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3712   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3713   PetscFunctionReturn(PETSC_SUCCESS);
3714 }
3715 
3716 /*@
3717   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3718 
3719   Not Collective
3720 
3721   Input Parameters:
3722 + dm - The `DMPLEX`
3723 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3724 
3725   Output Parameter:
3726 . size - The support size for point `p`
3727 
3728   Level: beginner
3729 
3730 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3731 @*/
3732 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3733 {
3734   DM_Plex *mesh = (DM_Plex *)dm->data;
3735 
3736   PetscFunctionBegin;
3737   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3738   PetscAssertPointer(size, 3);
3739   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3740   PetscFunctionReturn(PETSC_SUCCESS);
3741 }
3742 
3743 /*@
3744   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3745 
3746   Not Collective
3747 
3748   Input Parameters:
3749 + dm   - The `DMPLEX`
3750 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3751 - size - The support size for point `p`
3752 
3753   Level: beginner
3754 
3755   Note:
3756   This should be called after `DMPlexSetChart()`.
3757 
3758 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3759 @*/
3760 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3761 {
3762   DM_Plex *mesh = (DM_Plex *)dm->data;
3763 
3764   PetscFunctionBegin;
3765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3766   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3767   PetscFunctionReturn(PETSC_SUCCESS);
3768 }
3769 
3770 /*@C
3771   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3772 
3773   Not Collective
3774 
3775   Input Parameters:
3776 + dm - The `DMPLEX`
3777 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3778 
3779   Output Parameter:
3780 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3781 
3782   Level: beginner
3783 
3784   Fortran Notes:
3785   `support` must be declared with
3786 .vb
3787   PetscInt, pointer :: support(:)
3788 .ve
3789 
3790   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3791   `DMPlexRestoreSupport()` is not needed/available in C.
3792 
3793 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3794 @*/
3795 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3796 {
3797   DM_Plex *mesh = (DM_Plex *)dm->data;
3798   PetscInt off;
3799 
3800   PetscFunctionBegin;
3801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3802   PetscAssertPointer(support, 3);
3803   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3804   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3805   PetscFunctionReturn(PETSC_SUCCESS);
3806 }
3807 
3808 /*@
3809   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3810 
3811   Not Collective
3812 
3813   Input Parameters:
3814 + dm      - The `DMPLEX`
3815 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3816 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3817 
3818   Level: beginner
3819 
3820   Note:
3821   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3822 
3823 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3824 @*/
3825 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3826 {
3827   DM_Plex *mesh = (DM_Plex *)dm->data;
3828   PetscInt pStart, pEnd;
3829   PetscInt dof, off, c;
3830 
3831   PetscFunctionBegin;
3832   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3833   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3834   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3835   if (dof) PetscAssertPointer(support, 3);
3836   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3837   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);
3838   for (c = 0; c < dof; ++c) {
3839     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);
3840     mesh->supports[off + c] = support[c];
3841   }
3842   PetscFunctionReturn(PETSC_SUCCESS);
3843 }
3844 
3845 /*@
3846   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3847 
3848   Not Collective
3849 
3850   Input Parameters:
3851 + dm           - The `DMPLEX`
3852 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3853 . supportPos   - The local index in the cone where the point should be put
3854 - supportPoint - The mesh point to insert
3855 
3856   Level: beginner
3857 
3858 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3859 @*/
3860 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3861 {
3862   DM_Plex *mesh = (DM_Plex *)dm->data;
3863   PetscInt pStart, pEnd;
3864   PetscInt dof, off;
3865 
3866   PetscFunctionBegin;
3867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3868   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3869   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3870   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3871   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);
3872   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);
3873   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);
3874   mesh->supports[off + supportPos] = supportPoint;
3875   PetscFunctionReturn(PETSC_SUCCESS);
3876 }
3877 
3878 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3879 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3880 {
3881   switch (ct) {
3882   case DM_POLYTOPE_SEGMENT:
3883     if (o == -1) return -2;
3884     break;
3885   case DM_POLYTOPE_TRIANGLE:
3886     if (o == -3) return -1;
3887     if (o == -2) return -3;
3888     if (o == -1) return -2;
3889     break;
3890   case DM_POLYTOPE_QUADRILATERAL:
3891     if (o == -4) return -2;
3892     if (o == -3) return -1;
3893     if (o == -2) return -4;
3894     if (o == -1) return -3;
3895     break;
3896   default:
3897     return o;
3898   }
3899   return o;
3900 }
3901 
3902 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3903 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3904 {
3905   switch (ct) {
3906   case DM_POLYTOPE_SEGMENT:
3907     if ((o == -2) || (o == 1)) return -1;
3908     if (o == -1) return 0;
3909     break;
3910   case DM_POLYTOPE_TRIANGLE:
3911     if (o == -3) return -2;
3912     if (o == -2) return -1;
3913     if (o == -1) return -3;
3914     break;
3915   case DM_POLYTOPE_QUADRILATERAL:
3916     if (o == -4) return -2;
3917     if (o == -3) return -1;
3918     if (o == -2) return -4;
3919     if (o == -1) return -3;
3920     break;
3921   default:
3922     return o;
3923   }
3924   return o;
3925 }
3926 
3927 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3928 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3929 {
3930   PetscInt pStart, pEnd, p;
3931 
3932   PetscFunctionBegin;
3933   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3934   for (p = pStart; p < pEnd; ++p) {
3935     const PetscInt *cone, *ornt;
3936     PetscInt        coneSize, c;
3937 
3938     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3939     PetscCall(DMPlexGetCone(dm, p, &cone));
3940     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3941     for (c = 0; c < coneSize; ++c) {
3942       DMPolytopeType ct;
3943       const PetscInt o = ornt[c];
3944 
3945       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3946       switch (ct) {
3947       case DM_POLYTOPE_SEGMENT:
3948         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3949         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3950         break;
3951       case DM_POLYTOPE_TRIANGLE:
3952         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3953         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3954         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3955         break;
3956       case DM_POLYTOPE_QUADRILATERAL:
3957         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3958         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3959         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3960         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3961         break;
3962       default:
3963         break;
3964       }
3965     }
3966   }
3967   PetscFunctionReturn(PETSC_SUCCESS);
3968 }
3969 
3970 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3971 {
3972   DM_Plex *mesh = (DM_Plex *)dm->data;
3973 
3974   PetscFunctionBeginHot;
3975   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3976     if (useCone) {
3977       PetscCall(DMPlexGetConeSize(dm, p, size));
3978       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3979     } else {
3980       PetscCall(DMPlexGetSupportSize(dm, p, size));
3981       PetscCall(DMPlexGetSupport(dm, p, arr));
3982     }
3983   } else {
3984     if (useCone) {
3985       const PetscSection s   = mesh->coneSection;
3986       const PetscInt     ps  = p - s->pStart;
3987       const PetscInt     off = s->atlasOff[ps];
3988 
3989       *size = s->atlasDof[ps];
3990       *arr  = mesh->cones + off;
3991       *ornt = mesh->coneOrientations + off;
3992     } else {
3993       const PetscSection s   = mesh->supportSection;
3994       const PetscInt     ps  = p - s->pStart;
3995       const PetscInt     off = s->atlasOff[ps];
3996 
3997       *size = s->atlasDof[ps];
3998       *arr  = mesh->supports + off;
3999     }
4000   }
4001   PetscFunctionReturn(PETSC_SUCCESS);
4002 }
4003 
4004 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
4005 {
4006   DM_Plex *mesh = (DM_Plex *)dm->data;
4007 
4008   PetscFunctionBeginHot;
4009   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4010     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4011   }
4012   PetscFunctionReturn(PETSC_SUCCESS);
4013 }
4014 
4015 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4016 {
4017   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4018   PetscInt       *closure;
4019   const PetscInt *tmp = NULL, *tmpO = NULL;
4020   PetscInt        off = 0, tmpSize, t;
4021 
4022   PetscFunctionBeginHot;
4023   if (ornt) {
4024     PetscCall(DMPlexGetCellType(dm, p, &ct));
4025     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;
4026   }
4027   if (*points) {
4028     closure = *points;
4029   } else {
4030     PetscInt maxConeSize, maxSupportSize;
4031     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4032     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4033   }
4034   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4035   if (ct == DM_POLYTOPE_UNKNOWN) {
4036     closure[off++] = p;
4037     closure[off++] = 0;
4038     for (t = 0; t < tmpSize; ++t) {
4039       closure[off++] = tmp[t];
4040       closure[off++] = tmpO ? tmpO[t] : 0;
4041     }
4042   } else {
4043     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4044 
4045     /* We assume that cells with a valid type have faces with a valid type */
4046     closure[off++] = p;
4047     closure[off++] = ornt;
4048     for (t = 0; t < tmpSize; ++t) {
4049       DMPolytopeType ft;
4050 
4051       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4052       closure[off++] = tmp[arr[t]];
4053       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4054     }
4055   }
4056   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4057   if (numPoints) *numPoints = tmpSize + 1;
4058   if (points) *points = closure;
4059   PetscFunctionReturn(PETSC_SUCCESS);
4060 }
4061 
4062 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4063 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4064 {
4065   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4066   const PetscInt *cone, *ornt;
4067   PetscInt       *pts, *closure = NULL;
4068   DMPolytopeType  ft;
4069   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4070   PetscInt        dim, coneSize, c, d, clSize, cl;
4071 
4072   PetscFunctionBeginHot;
4073   PetscCall(DMGetDimension(dm, &dim));
4074   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4075   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4076   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4077   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4078   maxSize       = PetscMax(coneSeries, supportSeries);
4079   if (*points) {
4080     pts = *points;
4081   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4082   c        = 0;
4083   pts[c++] = point;
4084   pts[c++] = o;
4085   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4086   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4087   for (cl = 0; cl < clSize * 2; cl += 2) {
4088     pts[c++] = closure[cl];
4089     pts[c++] = closure[cl + 1];
4090   }
4091   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4092   for (cl = 0; cl < clSize * 2; cl += 2) {
4093     pts[c++] = closure[cl];
4094     pts[c++] = closure[cl + 1];
4095   }
4096   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4097   for (d = 2; d < coneSize; ++d) {
4098     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4099     pts[c++] = cone[arr[d * 2 + 0]];
4100     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4101   }
4102   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4103   if (dim >= 3) {
4104     for (d = 2; d < coneSize; ++d) {
4105       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4106       const PetscInt *fcone, *fornt;
4107       PetscInt        fconeSize, fc, i;
4108 
4109       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4110       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4111       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4112       for (fc = 0; fc < fconeSize; ++fc) {
4113         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4114         const PetscInt co = farr[fc * 2 + 1];
4115 
4116         for (i = 0; i < c; i += 2)
4117           if (pts[i] == cp) break;
4118         if (i == c) {
4119           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4120           pts[c++] = cp;
4121           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4122         }
4123       }
4124       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4125     }
4126   }
4127   *numPoints = c / 2;
4128   *points    = pts;
4129   PetscFunctionReturn(PETSC_SUCCESS);
4130 }
4131 
4132 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4133 {
4134   DMPolytopeType ct;
4135   PetscInt      *closure, *fifo;
4136   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4137   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4138   PetscInt       depth, maxSize;
4139 
4140   PetscFunctionBeginHot;
4141   PetscCall(DMPlexGetDepth(dm, &depth));
4142   if (depth == 1) {
4143     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4144     PetscFunctionReturn(PETSC_SUCCESS);
4145   }
4146   PetscCall(DMPlexGetCellType(dm, p, &ct));
4147   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;
4148   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4149     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4150     PetscFunctionReturn(PETSC_SUCCESS);
4151   }
4152   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4153   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4154   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4155   maxSize       = PetscMax(coneSeries, supportSeries);
4156   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4157   if (*points) {
4158     closure = *points;
4159   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4160   closure[closureSize++] = p;
4161   closure[closureSize++] = ornt;
4162   fifo[fifoSize++]       = p;
4163   fifo[fifoSize++]       = ornt;
4164   fifo[fifoSize++]       = ct;
4165   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4166   while (fifoSize - fifoStart) {
4167     const PetscInt       q    = fifo[fifoStart++];
4168     const PetscInt       o    = fifo[fifoStart++];
4169     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4170     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4171     const PetscInt      *tmp, *tmpO = NULL;
4172     PetscInt             tmpSize, t;
4173 
4174     if (PetscDefined(USE_DEBUG)) {
4175       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4176       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);
4177     }
4178     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4179     for (t = 0; t < tmpSize; ++t) {
4180       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4181       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4182       const PetscInt cp = tmp[ip];
4183       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4184       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4185       PetscInt       c;
4186 
4187       /* Check for duplicate */
4188       for (c = 0; c < closureSize; c += 2) {
4189         if (closure[c] == cp) break;
4190       }
4191       if (c == closureSize) {
4192         closure[closureSize++] = cp;
4193         closure[closureSize++] = co;
4194         fifo[fifoSize++]       = cp;
4195         fifo[fifoSize++]       = co;
4196         fifo[fifoSize++]       = ct;
4197       }
4198     }
4199     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4200   }
4201   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4202   if (numPoints) *numPoints = closureSize / 2;
4203   if (points) *points = closure;
4204   PetscFunctionReturn(PETSC_SUCCESS);
4205 }
4206 
4207 /*@C
4208   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4209 
4210   Not Collective
4211 
4212   Input Parameters:
4213 + dm      - The `DMPLEX`
4214 . p       - The mesh point
4215 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4216 
4217   Input/Output Parameter:
4218 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4219            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4220            otherwise the provided array is used to hold the values
4221 
4222   Output Parameter:
4223 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4224 
4225   Level: beginner
4226 
4227   Note:
4228   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4229 
4230   Fortran Notes:
4231   `points` must be declared with
4232 .vb
4233   PetscInt, pointer :: points(:)
4234 .ve
4235   and is always allocated by the function.
4236 
4237   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4238 
4239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4240 @*/
4241 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4242 {
4243   PetscFunctionBeginHot;
4244   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4245   if (numPoints) PetscAssertPointer(numPoints, 4);
4246   if (points) PetscAssertPointer(points, 5);
4247   if (PetscDefined(USE_DEBUG)) {
4248     PetscInt pStart, pEnd;
4249     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4250     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);
4251   }
4252   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4253   PetscFunctionReturn(PETSC_SUCCESS);
4254 }
4255 
4256 /*@C
4257   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4258 
4259   Not Collective
4260 
4261   Input Parameters:
4262 + dm        - The `DMPLEX`
4263 . p         - The mesh point
4264 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4265 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4266 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4267 
4268   Level: beginner
4269 
4270   Note:
4271   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4272 
4273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4274 @*/
4275 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4276 {
4277   PetscFunctionBeginHot;
4278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4279   if (numPoints) *numPoints = 0;
4280   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4281   PetscFunctionReturn(PETSC_SUCCESS);
4282 }
4283 
4284 /*@
4285   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4286 
4287   Not Collective
4288 
4289   Input Parameter:
4290 . dm - The `DMPLEX`
4291 
4292   Output Parameters:
4293 + maxConeSize    - The maximum number of in-edges
4294 - maxSupportSize - The maximum number of out-edges
4295 
4296   Level: beginner
4297 
4298 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4299 @*/
4300 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4301 {
4302   DM_Plex *mesh = (DM_Plex *)dm->data;
4303 
4304   PetscFunctionBegin;
4305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4306   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4307   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4308   PetscFunctionReturn(PETSC_SUCCESS);
4309 }
4310 
4311 PetscErrorCode DMSetUp_Plex(DM dm)
4312 {
4313   DM_Plex *mesh = (DM_Plex *)dm->data;
4314   PetscInt size, maxSupportSize;
4315 
4316   PetscFunctionBegin;
4317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4318   PetscCall(PetscSectionSetUp(mesh->coneSection));
4319   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4320   PetscCall(PetscMalloc1(size, &mesh->cones));
4321   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4322   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4323   if (maxSupportSize) {
4324     PetscCall(PetscSectionSetUp(mesh->supportSection));
4325     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4326     PetscCall(PetscMalloc1(size, &mesh->supports));
4327   }
4328   PetscFunctionReturn(PETSC_SUCCESS);
4329 }
4330 
4331 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4332 {
4333   PetscFunctionBegin;
4334   if (subdm) PetscCall(DMClone(dm, subdm));
4335   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4336   if (subdm) (*subdm)->useNatural = dm->useNatural;
4337   if (dm->useNatural && dm->sfMigration) {
4338     PetscSF sfNatural;
4339 
4340     (*subdm)->sfMigration = dm->sfMigration;
4341     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4342     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4343     (*subdm)->sfNatural = sfNatural;
4344   }
4345   PetscFunctionReturn(PETSC_SUCCESS);
4346 }
4347 
4348 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4349 {
4350   PetscInt i = 0;
4351 
4352   PetscFunctionBegin;
4353   PetscCall(DMClone(dms[0], superdm));
4354   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4355   (*superdm)->useNatural = PETSC_FALSE;
4356   for (i = 0; i < len; i++) {
4357     if (dms[i]->useNatural && dms[i]->sfMigration) {
4358       PetscSF sfNatural;
4359 
4360       (*superdm)->sfMigration = dms[i]->sfMigration;
4361       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4362       (*superdm)->useNatural = PETSC_TRUE;
4363       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4364       (*superdm)->sfNatural = sfNatural;
4365       break;
4366     }
4367   }
4368   PetscFunctionReturn(PETSC_SUCCESS);
4369 }
4370 
4371 /*@
4372   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4373 
4374   Not Collective
4375 
4376   Input Parameter:
4377 . dm - The `DMPLEX`
4378 
4379   Level: beginner
4380 
4381   Note:
4382   This should be called after all calls to `DMPlexSetCone()`
4383 
4384 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4385 @*/
4386 PetscErrorCode DMPlexSymmetrize(DM dm)
4387 {
4388   DM_Plex  *mesh = (DM_Plex *)dm->data;
4389   PetscInt *offsets;
4390   PetscInt  supportSize;
4391   PetscInt  pStart, pEnd, p;
4392 
4393   PetscFunctionBegin;
4394   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4395   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4396   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4397   /* Calculate support sizes */
4398   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4399   for (p = pStart; p < pEnd; ++p) {
4400     PetscInt dof, off, c;
4401 
4402     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4403     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4404     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4405   }
4406   PetscCall(PetscSectionSetUp(mesh->supportSection));
4407   /* Calculate supports */
4408   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4409   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4410   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4411   for (p = pStart; p < pEnd; ++p) {
4412     PetscInt dof, off, c;
4413 
4414     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4415     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4416     for (c = off; c < off + dof; ++c) {
4417       const PetscInt q = mesh->cones[c];
4418       PetscInt       offS;
4419 
4420       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4421 
4422       mesh->supports[offS + offsets[q]] = p;
4423       ++offsets[q];
4424     }
4425   }
4426   PetscCall(PetscFree(offsets));
4427   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4428   PetscFunctionReturn(PETSC_SUCCESS);
4429 }
4430 
4431 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4432 {
4433   IS stratumIS;
4434 
4435   PetscFunctionBegin;
4436   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4437   if (PetscDefined(USE_DEBUG)) {
4438     PetscInt  qStart, qEnd, numLevels, level;
4439     PetscBool overlap = PETSC_FALSE;
4440     PetscCall(DMLabelGetNumValues(label, &numLevels));
4441     for (level = 0; level < numLevels; level++) {
4442       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4443       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4444         overlap = PETSC_TRUE;
4445         break;
4446       }
4447     }
4448     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);
4449   }
4450   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4451   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4452   PetscCall(ISDestroy(&stratumIS));
4453   PetscFunctionReturn(PETSC_SUCCESS);
4454 }
4455 
4456 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4457 {
4458   PetscInt *pMin, *pMax;
4459   PetscInt  pStart, pEnd;
4460   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4461 
4462   PetscFunctionBegin;
4463   {
4464     DMLabel label2;
4465 
4466     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4467     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4468   }
4469   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4470   for (PetscInt p = pStart; p < pEnd; ++p) {
4471     DMPolytopeType ct;
4472 
4473     PetscCall(DMPlexGetCellType(dm, p, &ct));
4474     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4475     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4476   }
4477   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4478   for (PetscInt d = dmin; d <= dmax; ++d) {
4479     pMin[d] = PETSC_INT_MAX;
4480     pMax[d] = PETSC_INT_MIN;
4481   }
4482   for (PetscInt p = pStart; p < pEnd; ++p) {
4483     DMPolytopeType ct;
4484     PetscInt       d;
4485 
4486     PetscCall(DMPlexGetCellType(dm, p, &ct));
4487     d       = DMPolytopeTypeGetDim(ct);
4488     pMin[d] = PetscMin(p, pMin[d]);
4489     pMax[d] = PetscMax(p, pMax[d]);
4490   }
4491   for (PetscInt d = dmin; d <= dmax; ++d) {
4492     if (pMin[d] > pMax[d]) continue;
4493     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4494   }
4495   PetscCall(PetscFree2(pMin, pMax));
4496   PetscFunctionReturn(PETSC_SUCCESS);
4497 }
4498 
4499 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4500 {
4501   PetscInt pStart, pEnd;
4502   PetscInt numRoots = 0, numLeaves = 0;
4503 
4504   PetscFunctionBegin;
4505   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4506   {
4507     /* Initialize roots and count leaves */
4508     PetscInt sMin = PETSC_INT_MAX;
4509     PetscInt sMax = PETSC_INT_MIN;
4510     PetscInt coneSize, supportSize;
4511 
4512     for (PetscInt p = pStart; p < pEnd; ++p) {
4513       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4514       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4515       if (!coneSize && supportSize) {
4516         sMin = PetscMin(p, sMin);
4517         sMax = PetscMax(p, sMax);
4518         ++numRoots;
4519       } else if (!supportSize && coneSize) {
4520         ++numLeaves;
4521       } else if (!supportSize && !coneSize) {
4522         /* Isolated points */
4523         sMin = PetscMin(p, sMin);
4524         sMax = PetscMax(p, sMax);
4525       }
4526     }
4527     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4528   }
4529 
4530   if (numRoots + numLeaves == (pEnd - pStart)) {
4531     PetscInt sMin = PETSC_INT_MAX;
4532     PetscInt sMax = PETSC_INT_MIN;
4533     PetscInt coneSize, supportSize;
4534 
4535     for (PetscInt p = pStart; p < pEnd; ++p) {
4536       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4537       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4538       if (!supportSize && coneSize) {
4539         sMin = PetscMin(p, sMin);
4540         sMax = PetscMax(p, sMax);
4541       }
4542     }
4543     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4544   } else {
4545     PetscInt level = 0;
4546     PetscInt qStart, qEnd;
4547 
4548     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4549     while (qEnd > qStart) {
4550       PetscInt sMin = PETSC_INT_MAX;
4551       PetscInt sMax = PETSC_INT_MIN;
4552 
4553       for (PetscInt q = qStart; q < qEnd; ++q) {
4554         const PetscInt *support;
4555         PetscInt        supportSize;
4556 
4557         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4558         PetscCall(DMPlexGetSupport(dm, q, &support));
4559         for (PetscInt s = 0; s < supportSize; ++s) {
4560           sMin = PetscMin(support[s], sMin);
4561           sMax = PetscMax(support[s], sMax);
4562         }
4563       }
4564       PetscCall(DMLabelGetNumValues(label, &level));
4565       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4566       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4567     }
4568   }
4569   PetscFunctionReturn(PETSC_SUCCESS);
4570 }
4571 
4572 /*@
4573   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4574 
4575   Collective
4576 
4577   Input Parameter:
4578 . dm - The `DMPLEX`
4579 
4580   Level: beginner
4581 
4582   Notes:
4583   The strata group all points of the same grade, and this function calculates the strata. This
4584   grade can be seen as the height (or depth) of the point in the DAG.
4585 
4586   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4587   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4588   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4589   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4590   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4591   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4592   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4593 
4594   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4595   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4596   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
4597   to interpolate only that one (e0), so that
4598 .vb
4599   cone(c0) = {e0, v2}
4600   cone(e0) = {v0, v1}
4601 .ve
4602   If `DMPlexStratify()` is run on this mesh, it will give depths
4603 .vb
4604    depth 0 = {v0, v1, v2}
4605    depth 1 = {e0, c0}
4606 .ve
4607   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4608 
4609   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4610 
4611 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4612 @*/
4613 PetscErrorCode DMPlexStratify(DM dm)
4614 {
4615   DM_Plex  *mesh = (DM_Plex *)dm->data;
4616   DMLabel   label;
4617   PetscBool flg = PETSC_FALSE;
4618 
4619   PetscFunctionBegin;
4620   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4621   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4622 
4623   // Create depth label
4624   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4625   PetscCall(DMCreateLabel(dm, "depth"));
4626   PetscCall(DMPlexGetDepthLabel(dm, &label));
4627 
4628   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4629   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4630   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4631 
4632   { /* just in case there is an empty process */
4633     PetscInt numValues, maxValues = 0, v;
4634 
4635     PetscCall(DMLabelGetNumValues(label, &numValues));
4636     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4637     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4638   }
4639   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4640   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4641   PetscFunctionReturn(PETSC_SUCCESS);
4642 }
4643 
4644 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4645 {
4646   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4647   PetscInt       dim, depth, pheight, coneSize;
4648   PetscBool      preferTensor;
4649 
4650   PetscFunctionBeginHot;
4651   PetscCall(DMGetDimension(dm, &dim));
4652   PetscCall(DMPlexGetDepth(dm, &depth));
4653   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4654   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4655   pheight = depth - pdepth;
4656   if (depth <= 1) {
4657     switch (pdepth) {
4658     case 0:
4659       ct = DM_POLYTOPE_POINT;
4660       break;
4661     case 1:
4662       switch (coneSize) {
4663       case 2:
4664         ct = DM_POLYTOPE_SEGMENT;
4665         break;
4666       case 3:
4667         ct = DM_POLYTOPE_TRIANGLE;
4668         break;
4669       case 4:
4670         switch (dim) {
4671         case 2:
4672           ct = DM_POLYTOPE_QUADRILATERAL;
4673           break;
4674         case 3:
4675           ct = DM_POLYTOPE_TETRAHEDRON;
4676           break;
4677         default:
4678           break;
4679         }
4680         break;
4681       case 5:
4682         ct = DM_POLYTOPE_PYRAMID;
4683         break;
4684       case 6:
4685         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4686         break;
4687       case 8:
4688         ct = DM_POLYTOPE_HEXAHEDRON;
4689         break;
4690       default:
4691         break;
4692       }
4693     }
4694   } else {
4695     if (pdepth == 0) {
4696       ct = DM_POLYTOPE_POINT;
4697     } else if (pheight == 0) {
4698       switch (dim) {
4699       case 1:
4700         switch (coneSize) {
4701         case 2:
4702           ct = DM_POLYTOPE_SEGMENT;
4703           break;
4704         default:
4705           break;
4706         }
4707         break;
4708       case 2:
4709         switch (coneSize) {
4710         case 3:
4711           ct = DM_POLYTOPE_TRIANGLE;
4712           break;
4713         case 4:
4714           ct = DM_POLYTOPE_QUADRILATERAL;
4715           break;
4716         default:
4717           break;
4718         }
4719         break;
4720       case 3:
4721         switch (coneSize) {
4722         case 4:
4723           ct = DM_POLYTOPE_TETRAHEDRON;
4724           break;
4725         case 5: {
4726           const PetscInt *cone;
4727           PetscInt        faceConeSize;
4728 
4729           PetscCall(DMPlexGetCone(dm, p, &cone));
4730           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4731           switch (faceConeSize) {
4732           case 3:
4733             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4734             break;
4735           case 4:
4736             ct = DM_POLYTOPE_PYRAMID;
4737             break;
4738           }
4739         } break;
4740         case 6:
4741           ct = DM_POLYTOPE_HEXAHEDRON;
4742           break;
4743         default:
4744           break;
4745         }
4746         break;
4747       default:
4748         break;
4749       }
4750     } else if (pheight > 0) {
4751       switch (coneSize) {
4752       case 2:
4753         ct = DM_POLYTOPE_SEGMENT;
4754         break;
4755       case 3:
4756         ct = DM_POLYTOPE_TRIANGLE;
4757         break;
4758       case 4:
4759         ct = DM_POLYTOPE_QUADRILATERAL;
4760         break;
4761       default:
4762         break;
4763       }
4764     }
4765   }
4766   *pt = ct;
4767   PetscFunctionReturn(PETSC_SUCCESS);
4768 }
4769 
4770 /*@
4771   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4772 
4773   Collective
4774 
4775   Input Parameter:
4776 . dm - The `DMPLEX`
4777 
4778   Level: developer
4779 
4780   Note:
4781   This function is normally called automatically when a cell type is requested. It creates an
4782   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4783   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4784 
4785   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4786 
4787 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4788 @*/
4789 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4790 {
4791   DM_Plex *mesh;
4792   DMLabel  ctLabel;
4793   PetscInt pStart, pEnd, p;
4794 
4795   PetscFunctionBegin;
4796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4797   mesh = (DM_Plex *)dm->data;
4798   PetscCall(DMCreateLabel(dm, "celltype"));
4799   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4800   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4801   PetscCall(PetscFree(mesh->cellTypes));
4802   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4803   for (p = pStart; p < pEnd; ++p) {
4804     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4805     PetscInt       pdepth;
4806 
4807     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4808     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4809     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]);
4810     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4811     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4812   }
4813   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4814   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4815   PetscFunctionReturn(PETSC_SUCCESS);
4816 }
4817 
4818 /*@C
4819   DMPlexGetJoin - Get an array for the join of the set of points
4820 
4821   Not Collective
4822 
4823   Input Parameters:
4824 + dm        - The `DMPLEX` object
4825 . numPoints - The number of input points for the join
4826 - points    - The input points
4827 
4828   Output Parameters:
4829 + numCoveredPoints - The number of points in the join
4830 - coveredPoints    - The points in the join
4831 
4832   Level: intermediate
4833 
4834   Note:
4835   Currently, this is restricted to a single level join
4836 
4837   Fortran Notes:
4838   `converedPoints` must be declared with
4839 .vb
4840   PetscInt, pointer :: coveredPints(:)
4841 .ve
4842 
4843 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4844 @*/
4845 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4846 {
4847   DM_Plex  *mesh = (DM_Plex *)dm->data;
4848   PetscInt *join[2];
4849   PetscInt  joinSize, i = 0;
4850   PetscInt  dof, off, p, c, m;
4851   PetscInt  maxSupportSize;
4852 
4853   PetscFunctionBegin;
4854   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4855   PetscAssertPointer(points, 3);
4856   PetscAssertPointer(numCoveredPoints, 4);
4857   PetscAssertPointer(coveredPoints, 5);
4858   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4859   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4860   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4861   /* Copy in support of first point */
4862   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4863   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4864   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4865   /* Check each successive support */
4866   for (p = 1; p < numPoints; ++p) {
4867     PetscInt newJoinSize = 0;
4868 
4869     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4870     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4871     for (c = 0; c < dof; ++c) {
4872       const PetscInt point = mesh->supports[off + c];
4873 
4874       for (m = 0; m < joinSize; ++m) {
4875         if (point == join[i][m]) {
4876           join[1 - i][newJoinSize++] = point;
4877           break;
4878         }
4879       }
4880     }
4881     joinSize = newJoinSize;
4882     i        = 1 - i;
4883   }
4884   *numCoveredPoints = joinSize;
4885   *coveredPoints    = join[i];
4886   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4887   PetscFunctionReturn(PETSC_SUCCESS);
4888 }
4889 
4890 /*@C
4891   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4892 
4893   Not Collective
4894 
4895   Input Parameters:
4896 + dm        - The `DMPLEX` object
4897 . numPoints - The number of input points for the join
4898 - points    - The input points
4899 
4900   Output Parameters:
4901 + numCoveredPoints - The number of points in the join
4902 - coveredPoints    - The points in the join
4903 
4904   Level: intermediate
4905 
4906   Fortran Notes:
4907   `converedPoints` must be declared with
4908 .vb
4909   PetscInt, pointer :: coveredPoints(:)
4910 .ve
4911 
4912   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4913 
4914 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4915 @*/
4916 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4917 {
4918   PetscFunctionBegin;
4919   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4920   if (points) PetscAssertPointer(points, 3);
4921   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4922   PetscAssertPointer(coveredPoints, 5);
4923   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4924   if (numCoveredPoints) *numCoveredPoints = 0;
4925   PetscFunctionReturn(PETSC_SUCCESS);
4926 }
4927 
4928 /*@C
4929   DMPlexGetFullJoin - Get an array for the join of the set of points
4930 
4931   Not Collective
4932 
4933   Input Parameters:
4934 + dm        - The `DMPLEX` object
4935 . numPoints - The number of input points for the join
4936 - points    - The input points, its length is `numPoints`
4937 
4938   Output Parameters:
4939 + numCoveredPoints - The number of points in the join
4940 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4941 
4942   Level: intermediate
4943 
4944   Fortran Notes:
4945 .vb
4946   PetscInt, pointer :: coveredPints(:)
4947 .ve
4948 
4949 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4950 @*/
4951 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4952 {
4953   PetscInt *offsets, **closures;
4954   PetscInt *join[2];
4955   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4956   PetscInt  p, d, c, m, ms;
4957 
4958   PetscFunctionBegin;
4959   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4960   PetscAssertPointer(points, 3);
4961   PetscAssertPointer(numCoveredPoints, 4);
4962   PetscAssertPointer(coveredPoints, 5);
4963 
4964   PetscCall(DMPlexGetDepth(dm, &depth));
4965   PetscCall(PetscCalloc1(numPoints, &closures));
4966   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4967   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4968   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4969   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4970   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4971 
4972   for (p = 0; p < numPoints; ++p) {
4973     PetscInt closureSize;
4974 
4975     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4976 
4977     offsets[p * (depth + 2) + 0] = 0;
4978     for (d = 0; d < depth + 1; ++d) {
4979       PetscInt pStart, pEnd, i;
4980 
4981       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4982       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4983         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4984           offsets[p * (depth + 2) + d + 1] = i;
4985           break;
4986         }
4987       }
4988       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4989     }
4990     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);
4991   }
4992   for (d = 0; d < depth + 1; ++d) {
4993     PetscInt dof;
4994 
4995     /* Copy in support of first point */
4996     dof = offsets[d + 1] - offsets[d];
4997     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4998     /* Check each successive cone */
4999     for (p = 1; p < numPoints && joinSize; ++p) {
5000       PetscInt newJoinSize = 0;
5001 
5002       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
5003       for (c = 0; c < dof; ++c) {
5004         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
5005 
5006         for (m = 0; m < joinSize; ++m) {
5007           if (point == join[i][m]) {
5008             join[1 - i][newJoinSize++] = point;
5009             break;
5010           }
5011         }
5012       }
5013       joinSize = newJoinSize;
5014       i        = 1 - i;
5015     }
5016     if (joinSize) break;
5017   }
5018   *numCoveredPoints = joinSize;
5019   *coveredPoints    = join[i];
5020   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5021   PetscCall(PetscFree(closures));
5022   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5023   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5024   PetscFunctionReturn(PETSC_SUCCESS);
5025 }
5026 
5027 /*@C
5028   DMPlexGetMeet - Get an array for the meet of the set of points
5029 
5030   Not Collective
5031 
5032   Input Parameters:
5033 + dm        - The `DMPLEX` object
5034 . numPoints - The number of input points for the meet
5035 - points    - The input points, of length `numPoints`
5036 
5037   Output Parameters:
5038 + numCoveringPoints - The number of points in the meet
5039 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5040 
5041   Level: intermediate
5042 
5043   Note:
5044   Currently, this is restricted to a single level meet
5045 
5046   Fortran Note:
5047   `coveringPoints` must be declared with
5048 .vb
5049   PetscInt, pointer :: coveringPoints(:)
5050 .ve
5051 
5052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5053 @*/
5054 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5055 {
5056   DM_Plex  *mesh = (DM_Plex *)dm->data;
5057   PetscInt *meet[2];
5058   PetscInt  meetSize, i = 0;
5059   PetscInt  dof, off, p, c, m;
5060   PetscInt  maxConeSize;
5061 
5062   PetscFunctionBegin;
5063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5064   PetscAssertPointer(points, 3);
5065   PetscAssertPointer(numCoveringPoints, 4);
5066   PetscAssertPointer(coveringPoints, 5);
5067   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5068   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5069   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5070   /* Copy in cone of first point */
5071   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5072   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5073   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5074   /* Check each successive cone */
5075   for (p = 1; p < numPoints; ++p) {
5076     PetscInt newMeetSize = 0;
5077 
5078     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5079     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5080     for (c = 0; c < dof; ++c) {
5081       const PetscInt point = mesh->cones[off + c];
5082 
5083       for (m = 0; m < meetSize; ++m) {
5084         if (point == meet[i][m]) {
5085           meet[1 - i][newMeetSize++] = point;
5086           break;
5087         }
5088       }
5089     }
5090     meetSize = newMeetSize;
5091     i        = 1 - i;
5092   }
5093   *numCoveringPoints = meetSize;
5094   *coveringPoints    = meet[i];
5095   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5096   PetscFunctionReturn(PETSC_SUCCESS);
5097 }
5098 
5099 /*@C
5100   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5101 
5102   Not Collective
5103 
5104   Input Parameters:
5105 + dm        - The `DMPLEX` object
5106 . numPoints - The number of input points for the meet
5107 - points    - The input points
5108 
5109   Output Parameters:
5110 + numCoveredPoints - The number of points in the meet
5111 - coveredPoints    - The points in the meet
5112 
5113   Level: intermediate
5114 
5115 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5116 @*/
5117 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5118 {
5119   PetscFunctionBegin;
5120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5121   if (points) PetscAssertPointer(points, 3);
5122   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5123   PetscAssertPointer(coveredPoints, 5);
5124   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5125   if (numCoveredPoints) *numCoveredPoints = 0;
5126   PetscFunctionReturn(PETSC_SUCCESS);
5127 }
5128 
5129 /*@C
5130   DMPlexGetFullMeet - Get an array for the meet of the set of points
5131 
5132   Not Collective
5133 
5134   Input Parameters:
5135 + dm        - The `DMPLEX` object
5136 . numPoints - The number of input points for the meet
5137 - points    - The input points, of length  `numPoints`
5138 
5139   Output Parameters:
5140 + numCoveredPoints - The number of points in the meet
5141 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5142 
5143   Level: intermediate
5144 
5145   Fortran Notes:
5146   `coveredPoints` must be declared with
5147 .vb
5148   PetscInt, pointer :: coveredPoints(:)
5149 .ve
5150 
5151 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5152 @*/
5153 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5154 {
5155   PetscInt *offsets, **closures;
5156   PetscInt *meet[2];
5157   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5158   PetscInt  p, h, c, m, mc;
5159 
5160   PetscFunctionBegin;
5161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5162   PetscAssertPointer(points, 3);
5163   PetscAssertPointer(numCoveredPoints, 4);
5164   PetscAssertPointer(coveredPoints, 5);
5165 
5166   PetscCall(DMPlexGetDepth(dm, &height));
5167   PetscCall(PetscMalloc1(numPoints, &closures));
5168   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5169   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5170   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5171   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5172   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5173 
5174   for (p = 0; p < numPoints; ++p) {
5175     PetscInt closureSize;
5176 
5177     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5178 
5179     offsets[p * (height + 2) + 0] = 0;
5180     for (h = 0; h < height + 1; ++h) {
5181       PetscInt pStart, pEnd, i;
5182 
5183       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5184       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5185         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5186           offsets[p * (height + 2) + h + 1] = i;
5187           break;
5188         }
5189       }
5190       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5191     }
5192     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);
5193   }
5194   for (h = 0; h < height + 1; ++h) {
5195     PetscInt dof;
5196 
5197     /* Copy in cone of first point */
5198     dof = offsets[h + 1] - offsets[h];
5199     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5200     /* Check each successive cone */
5201     for (p = 1; p < numPoints && meetSize; ++p) {
5202       PetscInt newMeetSize = 0;
5203 
5204       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5205       for (c = 0; c < dof; ++c) {
5206         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5207 
5208         for (m = 0; m < meetSize; ++m) {
5209           if (point == meet[i][m]) {
5210             meet[1 - i][newMeetSize++] = point;
5211             break;
5212           }
5213         }
5214       }
5215       meetSize = newMeetSize;
5216       i        = 1 - i;
5217     }
5218     if (meetSize) break;
5219   }
5220   *numCoveredPoints = meetSize;
5221   *coveredPoints    = meet[i];
5222   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5223   PetscCall(PetscFree(closures));
5224   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5225   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5226   PetscFunctionReturn(PETSC_SUCCESS);
5227 }
5228 
5229 /*@
5230   DMPlexEqual - Determine if two `DM` have the same topology
5231 
5232   Not Collective
5233 
5234   Input Parameters:
5235 + dmA - A `DMPLEX` object
5236 - dmB - A `DMPLEX` object
5237 
5238   Output Parameter:
5239 . equal - `PETSC_TRUE` if the topologies are identical
5240 
5241   Level: intermediate
5242 
5243   Note:
5244   We are not solving graph isomorphism, so we do not permute.
5245 
5246 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5247 @*/
5248 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5249 {
5250   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5251 
5252   PetscFunctionBegin;
5253   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5254   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5255   PetscAssertPointer(equal, 3);
5256 
5257   *equal = PETSC_FALSE;
5258   PetscCall(DMPlexGetDepth(dmA, &depth));
5259   PetscCall(DMPlexGetDepth(dmB, &depthB));
5260   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5261   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5262   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5263   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5264   for (p = pStart; p < pEnd; ++p) {
5265     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5266     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5267 
5268     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5269     PetscCall(DMPlexGetCone(dmA, p, &cone));
5270     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5271     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5272     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5273     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5274     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5275     for (c = 0; c < coneSize; ++c) {
5276       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5277       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5278     }
5279     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5280     PetscCall(DMPlexGetSupport(dmA, p, &support));
5281     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5282     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5283     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5284     for (s = 0; s < supportSize; ++s) {
5285       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5286     }
5287   }
5288   *equal = PETSC_TRUE;
5289   PetscFunctionReturn(PETSC_SUCCESS);
5290 }
5291 
5292 /*@
5293   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5294 
5295   Not Collective
5296 
5297   Input Parameters:
5298 + dm         - The `DMPLEX`
5299 . cellDim    - The cell dimension
5300 - numCorners - The number of vertices on a cell
5301 
5302   Output Parameter:
5303 . numFaceVertices - The number of vertices on a face
5304 
5305   Level: developer
5306 
5307   Note:
5308   Of course this can only work for a restricted set of symmetric shapes
5309 
5310 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5311 @*/
5312 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5313 {
5314   MPI_Comm comm;
5315 
5316   PetscFunctionBegin;
5317   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5318   PetscAssertPointer(numFaceVertices, 4);
5319   switch (cellDim) {
5320   case 0:
5321     *numFaceVertices = 0;
5322     break;
5323   case 1:
5324     *numFaceVertices = 1;
5325     break;
5326   case 2:
5327     switch (numCorners) {
5328     case 3:                 /* triangle */
5329       *numFaceVertices = 2; /* Edge has 2 vertices */
5330       break;
5331     case 4:                 /* quadrilateral */
5332       *numFaceVertices = 2; /* Edge has 2 vertices */
5333       break;
5334     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5335       *numFaceVertices = 3; /* Edge has 3 vertices */
5336       break;
5337     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5338       *numFaceVertices = 3; /* Edge has 3 vertices */
5339       break;
5340     default:
5341       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5342     }
5343     break;
5344   case 3:
5345     switch (numCorners) {
5346     case 4:                 /* tetradehdron */
5347       *numFaceVertices = 3; /* Face has 3 vertices */
5348       break;
5349     case 6:                 /* tet cohesive cells */
5350       *numFaceVertices = 4; /* Face has 4 vertices */
5351       break;
5352     case 8:                 /* hexahedron */
5353       *numFaceVertices = 4; /* Face has 4 vertices */
5354       break;
5355     case 9:                 /* tet cohesive Lagrange cells */
5356       *numFaceVertices = 6; /* Face has 6 vertices */
5357       break;
5358     case 10:                /* quadratic tetrahedron */
5359       *numFaceVertices = 6; /* Face has 6 vertices */
5360       break;
5361     case 12:                /* hex cohesive Lagrange cells */
5362       *numFaceVertices = 6; /* Face has 6 vertices */
5363       break;
5364     case 18:                /* quadratic tet cohesive Lagrange cells */
5365       *numFaceVertices = 6; /* Face has 6 vertices */
5366       break;
5367     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5368       *numFaceVertices = 9; /* Face has 9 vertices */
5369       break;
5370     default:
5371       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5372     }
5373     break;
5374   default:
5375     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5376   }
5377   PetscFunctionReturn(PETSC_SUCCESS);
5378 }
5379 
5380 /*@
5381   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5382 
5383   Not Collective
5384 
5385   Input Parameter:
5386 . dm - The `DMPLEX` object
5387 
5388   Output Parameter:
5389 . depthLabel - The `DMLabel` recording point depth
5390 
5391   Level: developer
5392 
5393 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5394 @*/
5395 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5396 {
5397   PetscFunctionBegin;
5398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5399   PetscAssertPointer(depthLabel, 2);
5400   *depthLabel = dm->depthLabel;
5401   PetscFunctionReturn(PETSC_SUCCESS);
5402 }
5403 
5404 /*@
5405   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5406 
5407   Not Collective
5408 
5409   Input Parameter:
5410 . dm - The `DMPLEX` object
5411 
5412   Output Parameter:
5413 . depth - The number of strata (breadth first levels) in the DAG
5414 
5415   Level: developer
5416 
5417   Notes:
5418   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5419 
5420   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5421 
5422   An empty mesh gives -1.
5423 
5424 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5425 @*/
5426 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5427 {
5428   DM_Plex *mesh = (DM_Plex *)dm->data;
5429   DMLabel  label;
5430   PetscInt d = -1;
5431 
5432   PetscFunctionBegin;
5433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5434   PetscAssertPointer(depth, 2);
5435   if (mesh->tr) {
5436     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5437   } else {
5438     PetscCall(DMPlexGetDepthLabel(dm, &label));
5439     // Allow missing depths
5440     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5441     *depth = d;
5442   }
5443   PetscFunctionReturn(PETSC_SUCCESS);
5444 }
5445 
5446 /*@
5447   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5448 
5449   Not Collective
5450 
5451   Input Parameters:
5452 + dm    - The `DMPLEX` object
5453 - depth - The requested depth
5454 
5455   Output Parameters:
5456 + start - The first point at this `depth`
5457 - end   - One beyond the last point at this `depth`
5458 
5459   Level: developer
5460 
5461   Notes:
5462   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5463   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5464   higher dimension, e.g., "edges".
5465 
5466 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5467 @*/
5468 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5469 {
5470   DM_Plex *mesh = (DM_Plex *)dm->data;
5471   DMLabel  label;
5472   PetscInt pStart, pEnd;
5473 
5474   PetscFunctionBegin;
5475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5476   if (start) {
5477     PetscAssertPointer(start, 3);
5478     *start = 0;
5479   }
5480   if (end) {
5481     PetscAssertPointer(end, 4);
5482     *end = 0;
5483   }
5484   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5485   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5486   if (depth < 0) {
5487     if (start) *start = pStart;
5488     if (end) *end = pEnd;
5489     PetscFunctionReturn(PETSC_SUCCESS);
5490   }
5491   if (mesh->tr) {
5492     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5493   } else {
5494     PetscCall(DMPlexGetDepthLabel(dm, &label));
5495     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5496     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5497   }
5498   PetscFunctionReturn(PETSC_SUCCESS);
5499 }
5500 
5501 /*@
5502   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5503 
5504   Not Collective
5505 
5506   Input Parameters:
5507 + dm     - The `DMPLEX` object
5508 - height - The requested height
5509 
5510   Output Parameters:
5511 + start - The first point at this `height`
5512 - end   - One beyond the last point at this `height`
5513 
5514   Level: developer
5515 
5516   Notes:
5517   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5518   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5519   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5520 
5521 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5522 @*/
5523 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5524 {
5525   DMLabel  label;
5526   PetscInt depth, pStart, pEnd;
5527 
5528   PetscFunctionBegin;
5529   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5530   if (start) {
5531     PetscAssertPointer(start, 3);
5532     *start = 0;
5533   }
5534   if (end) {
5535     PetscAssertPointer(end, 4);
5536     *end = 0;
5537   }
5538   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5539   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5540   if (height < 0) {
5541     if (start) *start = pStart;
5542     if (end) *end = pEnd;
5543     PetscFunctionReturn(PETSC_SUCCESS);
5544   }
5545   PetscCall(DMPlexGetDepthLabel(dm, &label));
5546   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5547   else PetscCall(DMGetDimension(dm, &depth));
5548   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5549   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5550   PetscFunctionReturn(PETSC_SUCCESS);
5551 }
5552 
5553 /*@
5554   DMPlexGetPointDepth - Get the `depth` of a given point
5555 
5556   Not Collective
5557 
5558   Input Parameters:
5559 + dm    - The `DMPLEX` object
5560 - point - The point
5561 
5562   Output Parameter:
5563 . depth - The depth of the `point`
5564 
5565   Level: intermediate
5566 
5567 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5568 @*/
5569 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5570 {
5571   PetscFunctionBegin;
5572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5573   PetscAssertPointer(depth, 3);
5574   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5575   PetscFunctionReturn(PETSC_SUCCESS);
5576 }
5577 
5578 /*@
5579   DMPlexGetPointHeight - Get the `height` of a given point
5580 
5581   Not Collective
5582 
5583   Input Parameters:
5584 + dm    - The `DMPLEX` object
5585 - point - The point
5586 
5587   Output Parameter:
5588 . height - The height of the `point`
5589 
5590   Level: intermediate
5591 
5592 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5593 @*/
5594 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5595 {
5596   PetscInt n, pDepth;
5597 
5598   PetscFunctionBegin;
5599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5600   PetscAssertPointer(height, 3);
5601   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5602   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5603   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5604   PetscFunctionReturn(PETSC_SUCCESS);
5605 }
5606 
5607 /*@
5608   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5609 
5610   Not Collective
5611 
5612   Input Parameter:
5613 . dm - The `DMPLEX` object
5614 
5615   Output Parameter:
5616 . celltypeLabel - The `DMLabel` recording cell polytope type
5617 
5618   Level: developer
5619 
5620   Note:
5621   This function will trigger automatica computation of cell types. This can be disabled by calling
5622   `DMCreateLabel`(dm, "celltype") beforehand.
5623 
5624 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5625 @*/
5626 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5627 {
5628   PetscFunctionBegin;
5629   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5630   PetscAssertPointer(celltypeLabel, 2);
5631   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5632   *celltypeLabel = dm->celltypeLabel;
5633   PetscFunctionReturn(PETSC_SUCCESS);
5634 }
5635 
5636 /*@
5637   DMPlexGetCellType - Get the polytope type of a given cell
5638 
5639   Not Collective
5640 
5641   Input Parameters:
5642 + dm   - The `DMPLEX` object
5643 - cell - The cell
5644 
5645   Output Parameter:
5646 . celltype - The polytope type of the cell
5647 
5648   Level: intermediate
5649 
5650 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5651 @*/
5652 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5653 {
5654   DM_Plex *mesh = (DM_Plex *)dm->data;
5655   DMLabel  label;
5656   PetscInt ct;
5657 
5658   PetscFunctionBegin;
5659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5660   PetscAssertPointer(celltype, 3);
5661   if (mesh->tr) {
5662     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5663   } else {
5664     PetscInt pStart, pEnd;
5665 
5666     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5667     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5668       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5669       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5670       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5671       for (PetscInt p = pStart; p < pEnd; p++) {
5672         PetscCall(DMLabelGetValue(label, p, &ct));
5673         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5674       }
5675     }
5676     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5677     if (PetscDefined(USE_DEBUG)) {
5678       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5679       PetscCall(DMLabelGetValue(label, cell, &ct));
5680       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5681       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5682     }
5683   }
5684   PetscFunctionReturn(PETSC_SUCCESS);
5685 }
5686 
5687 /*@
5688   DMPlexSetCellType - Set the polytope type of a given cell
5689 
5690   Not Collective
5691 
5692   Input Parameters:
5693 + dm       - The `DMPLEX` object
5694 . cell     - The cell
5695 - celltype - The polytope type of the cell
5696 
5697   Level: advanced
5698 
5699   Note:
5700   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5701   is executed. This function will override the computed type. However, if automatic classification will not succeed
5702   and a user wants to manually specify all types, the classification must be disabled by calling
5703   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5704 
5705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5706 @*/
5707 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5708 {
5709   DM_Plex *mesh = (DM_Plex *)dm->data;
5710   DMLabel  label;
5711   PetscInt pStart, pEnd;
5712 
5713   PetscFunctionBegin;
5714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5715   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5716   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5717   PetscCall(DMLabelSetValue(label, cell, celltype));
5718   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5719   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5720   PetscFunctionReturn(PETSC_SUCCESS);
5721 }
5722 
5723 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5724 {
5725   PetscSection section;
5726   PetscInt     maxHeight;
5727   const char  *prefix;
5728 
5729   PetscFunctionBegin;
5730   PetscCall(DMClone(dm, cdm));
5731   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5732   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5733   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5734   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5735   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5736   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5737   PetscCall(DMSetLocalSection(*cdm, section));
5738   PetscCall(PetscSectionDestroy(&section));
5739 
5740   PetscCall(DMSetNumFields(*cdm, 1));
5741   PetscCall(DMCreateDS(*cdm));
5742   (*cdm)->cloneOpts = PETSC_TRUE;
5743   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5744   PetscFunctionReturn(PETSC_SUCCESS);
5745 }
5746 
5747 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5748 {
5749   Vec coordsLocal, cellCoordsLocal;
5750   DM  coordsDM, cellCoordsDM;
5751 
5752   PetscFunctionBegin;
5753   *field = NULL;
5754   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5755   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5756   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5757   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5758   if (coordsLocal && coordsDM) {
5759     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5760     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5761   }
5762   PetscFunctionReturn(PETSC_SUCCESS);
5763 }
5764 
5765 /*@
5766   DMPlexGetConeSection - Return a section which describes the layout of cone data
5767 
5768   Not Collective
5769 
5770   Input Parameter:
5771 . dm - The `DMPLEX` object
5772 
5773   Output Parameter:
5774 . section - The `PetscSection` object
5775 
5776   Level: developer
5777 
5778 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5779 @*/
5780 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5781 {
5782   DM_Plex *mesh = (DM_Plex *)dm->data;
5783 
5784   PetscFunctionBegin;
5785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5786   if (section) *section = mesh->coneSection;
5787   PetscFunctionReturn(PETSC_SUCCESS);
5788 }
5789 
5790 /*@
5791   DMPlexGetSupportSection - Return a section which describes the layout of support data
5792 
5793   Not Collective
5794 
5795   Input Parameter:
5796 . dm - The `DMPLEX` object
5797 
5798   Output Parameter:
5799 . section - The `PetscSection` object
5800 
5801   Level: developer
5802 
5803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5804 @*/
5805 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5806 {
5807   DM_Plex *mesh = (DM_Plex *)dm->data;
5808 
5809   PetscFunctionBegin;
5810   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5811   if (section) *section = mesh->supportSection;
5812   PetscFunctionReturn(PETSC_SUCCESS);
5813 }
5814 
5815 /*@C
5816   DMPlexGetCones - Return cone data
5817 
5818   Not Collective
5819 
5820   Input Parameter:
5821 . dm - The `DMPLEX` object
5822 
5823   Output Parameter:
5824 . cones - The cone for each point
5825 
5826   Level: developer
5827 
5828 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5829 @*/
5830 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5831 {
5832   DM_Plex *mesh = (DM_Plex *)dm->data;
5833 
5834   PetscFunctionBegin;
5835   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5836   if (cones) *cones = mesh->cones;
5837   PetscFunctionReturn(PETSC_SUCCESS);
5838 }
5839 
5840 /*@C
5841   DMPlexGetConeOrientations - Return cone orientation data
5842 
5843   Not Collective
5844 
5845   Input Parameter:
5846 . dm - The `DMPLEX` object
5847 
5848   Output Parameter:
5849 . coneOrientations - The array of cone orientations for all points
5850 
5851   Level: developer
5852 
5853   Notes:
5854   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5855   as returned by `DMPlexGetConeOrientation()`.
5856 
5857   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5858 
5859 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5860 @*/
5861 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5862 {
5863   DM_Plex *mesh = (DM_Plex *)dm->data;
5864 
5865   PetscFunctionBegin;
5866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5867   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5868   PetscFunctionReturn(PETSC_SUCCESS);
5869 }
5870 
5871 /* FEM Support */
5872 
5873 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5874 {
5875   PetscInt depth;
5876 
5877   PetscFunctionBegin;
5878   PetscCall(DMPlexGetDepth(plex, &depth));
5879   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5880   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5881   PetscFunctionReturn(PETSC_SUCCESS);
5882 }
5883 
5884 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5885 {
5886   PetscInt depth;
5887 
5888   PetscFunctionBegin;
5889   PetscCall(DMPlexGetDepth(plex, &depth));
5890   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5891   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5892   PetscFunctionReturn(PETSC_SUCCESS);
5893 }
5894 
5895 /*
5896  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5897  representing a line in the section.
5898 */
5899 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5900 {
5901   PetscObject  obj;
5902   PetscClassId id;
5903   PetscFE      fe = NULL;
5904 
5905   PetscFunctionBeginHot;
5906   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5907   PetscCall(DMGetField(dm, field, NULL, &obj));
5908   PetscCall(PetscObjectGetClassId(obj, &id));
5909   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5910 
5911   if (!fe) {
5912     /* Assume the full interpolated mesh is in the chart; lines in particular */
5913     /* An order k SEM disc has k-1 dofs on an edge */
5914     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5915     *k = *k / *Nc + 1;
5916   } else {
5917     PetscInt       dual_space_size, dim;
5918     PetscDualSpace dsp;
5919 
5920     PetscCall(DMGetDimension(dm, &dim));
5921     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5922     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5923     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5924     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5925     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5926   }
5927   PetscFunctionReturn(PETSC_SUCCESS);
5928 }
5929 
5930 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5931 {
5932   PetscFunctionBeginHot;
5933   if (tensor) {
5934     *dof = PetscPowInt(k + 1, dim);
5935   } else {
5936     switch (dim) {
5937     case 1:
5938       *dof = k + 1;
5939       break;
5940     case 2:
5941       *dof = ((k + 1) * (k + 2)) / 2;
5942       break;
5943     case 3:
5944       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5945       break;
5946     default:
5947       *dof = 0;
5948     }
5949   }
5950   PetscFunctionReturn(PETSC_SUCCESS);
5951 }
5952 
5953 /*@
5954   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5955   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5956   section provided (or the section of the `DM`).
5957 
5958   Input Parameters:
5959 + dm      - The `DM`
5960 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5961 - section - The `PetscSection` to reorder, or `NULL` for the default section
5962 
5963   Example:
5964   A typical interpolated single-quad mesh might order points as
5965 .vb
5966   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5967 
5968   v4 -- e6 -- v3
5969   |           |
5970   e7    c0    e8
5971   |           |
5972   v1 -- e5 -- v2
5973 .ve
5974 
5975   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5976   dofs in the order of points, e.g.,
5977 .vb
5978     c0 -> [0,1,2,3]
5979     v1 -> [4]
5980     ...
5981     e5 -> [8, 9]
5982 .ve
5983 
5984   which corresponds to the dofs
5985 .vb
5986     6   10  11  7
5987     13  2   3   15
5988     12  0   1   14
5989     4   8   9   5
5990 .ve
5991 
5992   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5993 .vb
5994   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5995 .ve
5996 
5997   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5998 .vb
5999    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6000 .ve
6001 
6002   Level: developer
6003 
6004   Notes:
6005   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6006   degree of the basis.
6007 
6008   This is required to run with libCEED.
6009 
6010 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6011 @*/
6012 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6013 {
6014   DMLabel   label;
6015   PetscInt  dim, depth = -1, eStart = -1, Nf;
6016   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6017 
6018   PetscFunctionBegin;
6019   PetscCall(DMGetDimension(dm, &dim));
6020   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6021   if (point < 0) {
6022     PetscInt sStart, sEnd;
6023 
6024     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6025     point = sEnd - sStart ? sStart : point;
6026   }
6027   PetscCall(DMPlexGetDepthLabel(dm, &label));
6028   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6029   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6030   if (depth == 1) {
6031     eStart = point;
6032   } else if (depth == dim) {
6033     const PetscInt *cone;
6034 
6035     PetscCall(DMPlexGetCone(dm, point, &cone));
6036     if (dim == 2) eStart = cone[0];
6037     else if (dim == 3) {
6038       const PetscInt *cone2;
6039       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6040       eStart = cone2[0];
6041     } 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);
6042   } 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);
6043 
6044   PetscCall(PetscSectionGetNumFields(section, &Nf));
6045   for (PetscInt d = 1; d <= dim; d++) {
6046     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6047     PetscInt *perm;
6048 
6049     for (f = 0; f < Nf; ++f) {
6050       PetscInt dof;
6051 
6052       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6053       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6054       if (!continuous && d < dim) continue;
6055       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6056       size += dof * Nc;
6057     }
6058     PetscCall(PetscMalloc1(size, &perm));
6059     for (f = 0; f < Nf; ++f) {
6060       switch (d) {
6061       case 1:
6062         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6063         if (!continuous && d < dim) continue;
6064         /*
6065          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6066          We want              [ vtx0; edge of length k-1; vtx1 ]
6067          */
6068         if (continuous) {
6069           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6070           for (i = 0; i < k - 1; i++)
6071             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6072           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6073           foffset = offset;
6074         } else {
6075           PetscInt dof;
6076 
6077           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6078           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6079           foffset = offset;
6080         }
6081         break;
6082       case 2:
6083         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6084         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6085         if (!continuous && d < dim) continue;
6086         /* The SEM order is
6087 
6088          v_lb, {e_b}, v_rb,
6089          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6090          v_lt, reverse {e_t}, v_rt
6091          */
6092         if (continuous) {
6093           const PetscInt of   = 0;
6094           const PetscInt oeb  = of + PetscSqr(k - 1);
6095           const PetscInt oer  = oeb + (k - 1);
6096           const PetscInt oet  = oer + (k - 1);
6097           const PetscInt oel  = oet + (k - 1);
6098           const PetscInt ovlb = oel + (k - 1);
6099           const PetscInt ovrb = ovlb + 1;
6100           const PetscInt ovrt = ovrb + 1;
6101           const PetscInt ovlt = ovrt + 1;
6102           PetscInt       o;
6103 
6104           /* bottom */
6105           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6106           for (o = oeb; o < oer; ++o)
6107             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6108           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6109           /* middle */
6110           for (i = 0; i < k - 1; ++i) {
6111             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6112             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6113               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6114             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6115           }
6116           /* top */
6117           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6118           for (o = oel - 1; o >= oet; --o)
6119             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6120           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6121           foffset = offset;
6122         } else {
6123           PetscInt dof;
6124 
6125           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6126           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6127           foffset = offset;
6128         }
6129         break;
6130       case 3:
6131         /* The original hex closure is
6132 
6133          {c,
6134          f_b, f_t, f_f, f_b, f_r, f_l,
6135          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6136          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6137          */
6138         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6139         if (!continuous && d < dim) continue;
6140         /* The SEM order is
6141          Bottom Slice
6142          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6143          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6144          v_blb, {e_bb}, v_brb,
6145 
6146          Middle Slice (j)
6147          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6148          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6149          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6150 
6151          Top Slice
6152          v_tlf, {e_tf}, v_trf,
6153          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6154          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6155          */
6156         if (continuous) {
6157           const PetscInt oc    = 0;
6158           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6159           const PetscInt oft   = ofb + PetscSqr(k - 1);
6160           const PetscInt off   = oft + PetscSqr(k - 1);
6161           const PetscInt ofk   = off + PetscSqr(k - 1);
6162           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6163           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6164           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6165           const PetscInt oebb  = oebl + (k - 1);
6166           const PetscInt oebr  = oebb + (k - 1);
6167           const PetscInt oebf  = oebr + (k - 1);
6168           const PetscInt oetf  = oebf + (k - 1);
6169           const PetscInt oetr  = oetf + (k - 1);
6170           const PetscInt oetb  = oetr + (k - 1);
6171           const PetscInt oetl  = oetb + (k - 1);
6172           const PetscInt oerf  = oetl + (k - 1);
6173           const PetscInt oelf  = oerf + (k - 1);
6174           const PetscInt oelb  = oelf + (k - 1);
6175           const PetscInt oerb  = oelb + (k - 1);
6176           const PetscInt ovblf = oerb + (k - 1);
6177           const PetscInt ovblb = ovblf + 1;
6178           const PetscInt ovbrb = ovblb + 1;
6179           const PetscInt ovbrf = ovbrb + 1;
6180           const PetscInt ovtlf = ovbrf + 1;
6181           const PetscInt ovtrf = ovtlf + 1;
6182           const PetscInt ovtrb = ovtrf + 1;
6183           const PetscInt ovtlb = ovtrb + 1;
6184           PetscInt       o, n;
6185 
6186           /* Bottom Slice */
6187           /*   bottom */
6188           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6189           for (o = oetf - 1; o >= oebf; --o)
6190             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6191           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6192           /*   middle */
6193           for (i = 0; i < k - 1; ++i) {
6194             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6195             for (n = 0; n < k - 1; ++n) {
6196               o = ofb + n * (k - 1) + i;
6197               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6198             }
6199             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6200           }
6201           /*   top */
6202           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6203           for (o = oebb; o < oebr; ++o)
6204             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6205           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6206 
6207           /* Middle Slice */
6208           for (j = 0; j < k - 1; ++j) {
6209             /*   bottom */
6210             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6211             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6212               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6213             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6214             /*   middle */
6215             for (i = 0; i < k - 1; ++i) {
6216               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6217               for (n = 0; n < k - 1; ++n)
6218                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6219               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6220             }
6221             /*   top */
6222             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6223             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6224               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6225             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6226           }
6227 
6228           /* Top Slice */
6229           /*   bottom */
6230           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6231           for (o = oetf; o < oetr; ++o)
6232             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6233           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6234           /*   middle */
6235           for (i = 0; i < k - 1; ++i) {
6236             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6237             for (n = 0; n < k - 1; ++n)
6238               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6239             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6240           }
6241           /*   top */
6242           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6243           for (o = oetl - 1; o >= oetb; --o)
6244             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6245           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6246 
6247           foffset = offset;
6248         } else {
6249           PetscInt dof;
6250 
6251           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6252           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6253           foffset = offset;
6254         }
6255         break;
6256       default:
6257         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6258       }
6259     }
6260     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6261     /* Check permutation */
6262     {
6263       PetscInt *check;
6264 
6265       PetscCall(PetscMalloc1(size, &check));
6266       for (i = 0; i < size; ++i) {
6267         check[i] = -1;
6268         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6269       }
6270       for (i = 0; i < size; ++i) check[perm[i]] = i;
6271       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6272       PetscCall(PetscFree(check));
6273     }
6274     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6275     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6276       PetscInt *loc_perm;
6277       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6278       for (PetscInt i = 0; i < size; i++) {
6279         loc_perm[i]        = perm[i];
6280         loc_perm[size + i] = size + perm[i];
6281       }
6282       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6283     }
6284   }
6285   PetscFunctionReturn(PETSC_SUCCESS);
6286 }
6287 
6288 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6289 {
6290   PetscDS  prob;
6291   PetscInt depth, Nf, h;
6292   DMLabel  label;
6293 
6294   PetscFunctionBeginHot;
6295   PetscCall(DMGetDS(dm, &prob));
6296   Nf      = prob->Nf;
6297   label   = dm->depthLabel;
6298   *dspace = NULL;
6299   if (field < Nf) {
6300     PetscObject disc = prob->disc[field];
6301 
6302     if (disc->classid == PETSCFE_CLASSID) {
6303       PetscDualSpace dsp;
6304 
6305       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6306       PetscCall(DMLabelGetNumValues(label, &depth));
6307       PetscCall(DMLabelGetValue(label, point, &h));
6308       h = depth - 1 - h;
6309       if (h) {
6310         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6311       } else {
6312         *dspace = dsp;
6313       }
6314     }
6315   }
6316   PetscFunctionReturn(PETSC_SUCCESS);
6317 }
6318 
6319 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6320 {
6321   PetscScalar       *array;
6322   const PetscScalar *vArray;
6323   const PetscInt    *cone, *coneO;
6324   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6325 
6326   PetscFunctionBeginHot;
6327   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6328   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6329   PetscCall(DMPlexGetCone(dm, point, &cone));
6330   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6331   if (!values || !*values) {
6332     if ((point >= pStart) && (point < pEnd)) {
6333       PetscInt dof;
6334 
6335       PetscCall(PetscSectionGetDof(section, point, &dof));
6336       size += dof;
6337     }
6338     for (p = 0; p < numPoints; ++p) {
6339       const PetscInt cp = cone[p];
6340       PetscInt       dof;
6341 
6342       if ((cp < pStart) || (cp >= pEnd)) continue;
6343       PetscCall(PetscSectionGetDof(section, cp, &dof));
6344       size += dof;
6345     }
6346     if (!values) {
6347       if (csize) *csize = size;
6348       PetscFunctionReturn(PETSC_SUCCESS);
6349     }
6350     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6351   } else {
6352     array = *values;
6353   }
6354   size = 0;
6355   PetscCall(VecGetArrayRead(v, &vArray));
6356   if ((point >= pStart) && (point < pEnd)) {
6357     PetscInt           dof, off, d;
6358     const PetscScalar *varr;
6359 
6360     PetscCall(PetscSectionGetDof(section, point, &dof));
6361     PetscCall(PetscSectionGetOffset(section, point, &off));
6362     varr = PetscSafePointerPlusOffset(vArray, off);
6363     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6364     size += dof;
6365   }
6366   for (p = 0; p < numPoints; ++p) {
6367     const PetscInt     cp = cone[p];
6368     PetscInt           o  = coneO[p];
6369     PetscInt           dof, off, d;
6370     const PetscScalar *varr;
6371 
6372     if ((cp < pStart) || (cp >= pEnd)) continue;
6373     PetscCall(PetscSectionGetDof(section, cp, &dof));
6374     PetscCall(PetscSectionGetOffset(section, cp, &off));
6375     varr = PetscSafePointerPlusOffset(vArray, off);
6376     if (o >= 0) {
6377       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6378     } else {
6379       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6380     }
6381     size += dof;
6382   }
6383   PetscCall(VecRestoreArrayRead(v, &vArray));
6384   if (!*values) {
6385     if (csize) *csize = size;
6386     *values = array;
6387   } else {
6388     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6389     *csize = size;
6390   }
6391   PetscFunctionReturn(PETSC_SUCCESS);
6392 }
6393 
6394 /* Compress out points not in the section */
6395 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6396 {
6397   const PetscInt np = *numPoints;
6398   PetscInt       pStart, pEnd, p, q;
6399 
6400   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6401   for (p = 0, q = 0; p < np; ++p) {
6402     const PetscInt r = points[p * 2];
6403     if ((r >= pStart) && (r < pEnd)) {
6404       points[q * 2]     = r;
6405       points[q * 2 + 1] = points[p * 2 + 1];
6406       ++q;
6407     }
6408   }
6409   *numPoints = q;
6410   return PETSC_SUCCESS;
6411 }
6412 
6413 /* Compressed closure does not apply closure permutation */
6414 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6415 {
6416   const PetscInt *cla = NULL;
6417   PetscInt        np, *pts = NULL;
6418 
6419   PetscFunctionBeginHot;
6420   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6421   if (!ornt && *clPoints) {
6422     PetscInt dof, off;
6423 
6424     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6425     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6426     PetscCall(ISGetIndices(*clPoints, &cla));
6427     np  = dof / 2;
6428     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6429   } else {
6430     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6431     PetscCall(CompressPoints_Private(section, &np, pts));
6432   }
6433   *numPoints = np;
6434   *points    = pts;
6435   *clp       = cla;
6436   PetscFunctionReturn(PETSC_SUCCESS);
6437 }
6438 
6439 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6440 {
6441   PetscFunctionBeginHot;
6442   if (!*clPoints) {
6443     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6444   } else {
6445     PetscCall(ISRestoreIndices(*clPoints, clp));
6446   }
6447   *numPoints = 0;
6448   *points    = NULL;
6449   *clSec     = NULL;
6450   *clPoints  = NULL;
6451   *clp       = NULL;
6452   PetscFunctionReturn(PETSC_SUCCESS);
6453 }
6454 
6455 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6456 {
6457   PetscInt            offset = 0, p;
6458   const PetscInt    **perms  = NULL;
6459   const PetscScalar **flips  = NULL;
6460 
6461   PetscFunctionBeginHot;
6462   *size = 0;
6463   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6464   for (p = 0; p < numPoints; p++) {
6465     const PetscInt     point = points[2 * p];
6466     const PetscInt    *perm  = perms ? perms[p] : NULL;
6467     const PetscScalar *flip  = flips ? flips[p] : NULL;
6468     PetscInt           dof, off, d;
6469     const PetscScalar *varr;
6470 
6471     PetscCall(PetscSectionGetDof(section, point, &dof));
6472     PetscCall(PetscSectionGetOffset(section, point, &off));
6473     varr = PetscSafePointerPlusOffset(vArray, off);
6474     if (clperm) {
6475       if (perm) {
6476         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6477       } else {
6478         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6479       }
6480       if (flip) {
6481         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6482       }
6483     } else {
6484       if (perm) {
6485         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6486       } else {
6487         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6488       }
6489       if (flip) {
6490         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6491       }
6492     }
6493     offset += dof;
6494   }
6495   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6496   *size = offset;
6497   PetscFunctionReturn(PETSC_SUCCESS);
6498 }
6499 
6500 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[])
6501 {
6502   PetscInt offset = 0, f;
6503 
6504   PetscFunctionBeginHot;
6505   *size = 0;
6506   for (f = 0; f < numFields; ++f) {
6507     PetscInt            p;
6508     const PetscInt    **perms = NULL;
6509     const PetscScalar **flips = NULL;
6510 
6511     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6512     for (p = 0; p < numPoints; p++) {
6513       const PetscInt     point = points[2 * p];
6514       PetscInt           fdof, foff, b;
6515       const PetscScalar *varr;
6516       const PetscInt    *perm = perms ? perms[p] : NULL;
6517       const PetscScalar *flip = flips ? flips[p] : NULL;
6518 
6519       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6520       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6521       varr = &vArray[foff];
6522       if (clperm) {
6523         if (perm) {
6524           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6525         } else {
6526           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6527         }
6528         if (flip) {
6529           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6530         }
6531       } else {
6532         if (perm) {
6533           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6534         } else {
6535           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6536         }
6537         if (flip) {
6538           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6539         }
6540       }
6541       offset += fdof;
6542     }
6543     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6544   }
6545   *size = offset;
6546   PetscFunctionReturn(PETSC_SUCCESS);
6547 }
6548 
6549 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6550 {
6551   PetscSection    clSection;
6552   IS              clPoints;
6553   PetscInt       *points = NULL;
6554   const PetscInt *clp, *perm = NULL;
6555   PetscInt        depth, numFields, numPoints, asize;
6556 
6557   PetscFunctionBeginHot;
6558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6559   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6560   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6561   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6562   PetscCall(DMPlexGetDepth(dm, &depth));
6563   PetscCall(PetscSectionGetNumFields(section, &numFields));
6564   if (depth == 1 && numFields < 2) {
6565     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6566     PetscFunctionReturn(PETSC_SUCCESS);
6567   }
6568   /* Get points */
6569   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6570   /* Get sizes */
6571   asize = 0;
6572   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6573     PetscInt dof;
6574     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6575     asize += dof;
6576   }
6577   if (values) {
6578     const PetscScalar *vArray;
6579     PetscInt           size;
6580 
6581     if (*values) {
6582       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);
6583     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6584     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6585     PetscCall(VecGetArrayRead(v, &vArray));
6586     /* Get values */
6587     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6588     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6589     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6590     /* Cleanup array */
6591     PetscCall(VecRestoreArrayRead(v, &vArray));
6592   }
6593   if (csize) *csize = asize;
6594   /* Cleanup points */
6595   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6596   PetscFunctionReturn(PETSC_SUCCESS);
6597 }
6598 
6599 /*@C
6600   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6601 
6602   Not collective
6603 
6604   Input Parameters:
6605 + dm      - The `DM`
6606 . section - The section describing the layout in `v`, or `NULL` to use the default section
6607 . v       - The local vector
6608 - point   - The point in the `DM`
6609 
6610   Input/Output Parameters:
6611 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6612 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6613            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6614 
6615   Level: intermediate
6616 
6617   Notes:
6618   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6619   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6620   assembly function, and a user may already have allocated storage for this operation.
6621 
6622   A typical use could be
6623 .vb
6624    values = NULL;
6625    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6626    for (cl = 0; cl < clSize; ++cl) {
6627      <Compute on closure>
6628    }
6629    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6630 .ve
6631   or
6632 .vb
6633    PetscMalloc1(clMaxSize, &values);
6634    for (p = pStart; p < pEnd; ++p) {
6635      clSize = clMaxSize;
6636      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6637      for (cl = 0; cl < clSize; ++cl) {
6638        <Compute on closure>
6639      }
6640    }
6641    PetscFree(values);
6642 .ve
6643 
6644   Fortran Notes:
6645   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6646   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6647 
6648   `values` must be declared with
6649 .vb
6650   PetscScalar,dimension(:),pointer   :: values
6651 .ve
6652   and it will be allocated internally by PETSc to hold the values returned
6653 
6654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6655 @*/
6656 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6657 {
6658   PetscFunctionBeginHot;
6659   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6660   PetscFunctionReturn(PETSC_SUCCESS);
6661 }
6662 
6663 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6664 {
6665   DMLabel            depthLabel;
6666   PetscSection       clSection;
6667   IS                 clPoints;
6668   PetscScalar       *array;
6669   const PetscScalar *vArray;
6670   PetscInt          *points = NULL;
6671   const PetscInt    *clp, *perm = NULL;
6672   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6673 
6674   PetscFunctionBeginHot;
6675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6676   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6677   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6678   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6679   PetscCall(DMPlexGetDepth(dm, &mdepth));
6680   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6681   PetscCall(PetscSectionGetNumFields(section, &numFields));
6682   if (mdepth == 1 && numFields < 2) {
6683     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6684     PetscFunctionReturn(PETSC_SUCCESS);
6685   }
6686   /* Get points */
6687   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6688   for (clsize = 0, p = 0; p < Np; p++) {
6689     PetscInt dof;
6690     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6691     clsize += dof;
6692   }
6693   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6694   /* Filter points */
6695   for (p = 0; p < numPoints * 2; p += 2) {
6696     PetscInt dep;
6697 
6698     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6699     if (dep != depth) continue;
6700     points[Np * 2 + 0] = points[p];
6701     points[Np * 2 + 1] = points[p + 1];
6702     ++Np;
6703   }
6704   /* Get array */
6705   if (!values || !*values) {
6706     PetscInt asize = 0, dof;
6707 
6708     for (p = 0; p < Np * 2; p += 2) {
6709       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6710       asize += dof;
6711     }
6712     if (!values) {
6713       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6714       if (csize) *csize = asize;
6715       PetscFunctionReturn(PETSC_SUCCESS);
6716     }
6717     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6718   } else {
6719     array = *values;
6720   }
6721   PetscCall(VecGetArrayRead(v, &vArray));
6722   /* Get values */
6723   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6724   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6725   /* Cleanup points */
6726   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6727   /* Cleanup array */
6728   PetscCall(VecRestoreArrayRead(v, &vArray));
6729   if (!*values) {
6730     if (csize) *csize = size;
6731     *values = array;
6732   } else {
6733     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6734     *csize = size;
6735   }
6736   PetscFunctionReturn(PETSC_SUCCESS);
6737 }
6738 
6739 /*@C
6740   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6741 
6742   Not collective
6743 
6744   Input Parameters:
6745 + dm      - The `DM`
6746 . section - The section describing the layout in `v`, or `NULL` to use the default section
6747 . v       - The local vector
6748 . point   - The point in the `DM`
6749 . csize   - The number of values in the closure, or `NULL`
6750 - values  - The array of values
6751 
6752   Level: intermediate
6753 
6754   Note:
6755   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6756 
6757   Fortran Note:
6758   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6759   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6760 
6761 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6762 @*/
6763 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6764 {
6765   PetscInt size = 0;
6766 
6767   PetscFunctionBegin;
6768   /* Should work without recalculating size */
6769   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6770   *values = NULL;
6771   PetscFunctionReturn(PETSC_SUCCESS);
6772 }
6773 
6774 static inline void add(PetscScalar *x, PetscScalar y)
6775 {
6776   *x += y;
6777 }
6778 static inline void insert(PetscScalar *x, PetscScalar y)
6779 {
6780   *x = y;
6781 }
6782 
6783 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[])
6784 {
6785   PetscInt        cdof;  /* The number of constraints on this point */
6786   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6787   PetscScalar    *a;
6788   PetscInt        off, cind = 0, k;
6789 
6790   PetscFunctionBegin;
6791   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6792   PetscCall(PetscSectionGetOffset(section, point, &off));
6793   a = &array[off];
6794   if (!cdof || setBC) {
6795     if (clperm) {
6796       if (perm) {
6797         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6798       } else {
6799         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6800       }
6801     } else {
6802       if (perm) {
6803         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6804       } else {
6805         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6806       }
6807     }
6808   } else {
6809     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6810     if (clperm) {
6811       if (perm) {
6812         for (k = 0; k < dof; ++k) {
6813           if ((cind < cdof) && (k == cdofs[cind])) {
6814             ++cind;
6815             continue;
6816           }
6817           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6818         }
6819       } else {
6820         for (k = 0; k < dof; ++k) {
6821           if ((cind < cdof) && (k == cdofs[cind])) {
6822             ++cind;
6823             continue;
6824           }
6825           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6826         }
6827       }
6828     } else {
6829       if (perm) {
6830         for (k = 0; k < dof; ++k) {
6831           if ((cind < cdof) && (k == cdofs[cind])) {
6832             ++cind;
6833             continue;
6834           }
6835           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6836         }
6837       } else {
6838         for (k = 0; k < dof; ++k) {
6839           if ((cind < cdof) && (k == cdofs[cind])) {
6840             ++cind;
6841             continue;
6842           }
6843           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6844         }
6845       }
6846     }
6847   }
6848   PetscFunctionReturn(PETSC_SUCCESS);
6849 }
6850 
6851 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[])
6852 {
6853   PetscInt        cdof;  /* The number of constraints on this point */
6854   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6855   PetscScalar    *a;
6856   PetscInt        off, cind = 0, k;
6857 
6858   PetscFunctionBegin;
6859   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6860   PetscCall(PetscSectionGetOffset(section, point, &off));
6861   a = &array[off];
6862   if (cdof) {
6863     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6864     if (clperm) {
6865       if (perm) {
6866         for (k = 0; k < dof; ++k) {
6867           if ((cind < cdof) && (k == cdofs[cind])) {
6868             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6869             cind++;
6870           }
6871         }
6872       } else {
6873         for (k = 0; k < dof; ++k) {
6874           if ((cind < cdof) && (k == cdofs[cind])) {
6875             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6876             cind++;
6877           }
6878         }
6879       }
6880     } else {
6881       if (perm) {
6882         for (k = 0; k < dof; ++k) {
6883           if ((cind < cdof) && (k == cdofs[cind])) {
6884             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6885             cind++;
6886           }
6887         }
6888       } else {
6889         for (k = 0; k < dof; ++k) {
6890           if ((cind < cdof) && (k == cdofs[cind])) {
6891             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6892             cind++;
6893           }
6894         }
6895       }
6896     }
6897   }
6898   PetscFunctionReturn(PETSC_SUCCESS);
6899 }
6900 
6901 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[])
6902 {
6903   PetscScalar    *a;
6904   PetscInt        fdof, foff, fcdof, foffset = *offset;
6905   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6906   PetscInt        cind = 0, b;
6907 
6908   PetscFunctionBegin;
6909   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6910   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6911   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6912   a = &array[foff];
6913   if (!fcdof || setBC) {
6914     if (clperm) {
6915       if (perm) {
6916         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6917       } else {
6918         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6919       }
6920     } else {
6921       if (perm) {
6922         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6923       } else {
6924         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6925       }
6926     }
6927   } else {
6928     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6929     if (clperm) {
6930       if (perm) {
6931         for (b = 0; b < fdof; b++) {
6932           if ((cind < fcdof) && (b == fcdofs[cind])) {
6933             ++cind;
6934             continue;
6935           }
6936           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6937         }
6938       } else {
6939         for (b = 0; b < fdof; b++) {
6940           if ((cind < fcdof) && (b == fcdofs[cind])) {
6941             ++cind;
6942             continue;
6943           }
6944           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6945         }
6946       }
6947     } else {
6948       if (perm) {
6949         for (b = 0; b < fdof; b++) {
6950           if ((cind < fcdof) && (b == fcdofs[cind])) {
6951             ++cind;
6952             continue;
6953           }
6954           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6955         }
6956       } else {
6957         for (b = 0; b < fdof; b++) {
6958           if ((cind < fcdof) && (b == fcdofs[cind])) {
6959             ++cind;
6960             continue;
6961           }
6962           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6963         }
6964       }
6965     }
6966   }
6967   *offset += fdof;
6968   PetscFunctionReturn(PETSC_SUCCESS);
6969 }
6970 
6971 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[])
6972 {
6973   PetscScalar    *a;
6974   PetscInt        fdof, foff, fcdof, foffset = *offset;
6975   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6976   PetscInt        Nc, cind = 0, ncind = 0, b;
6977   PetscBool       ncSet, fcSet;
6978 
6979   PetscFunctionBegin;
6980   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6981   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6982   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6983   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6984   a = &array[foff];
6985   if (fcdof) {
6986     /* We just override fcdof and fcdofs with Ncc and comps */
6987     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6988     if (clperm) {
6989       if (perm) {
6990         if (comps) {
6991           for (b = 0; b < fdof; b++) {
6992             ncSet = fcSet = PETSC_FALSE;
6993             if (b % Nc == comps[ncind]) {
6994               ncind = (ncind + 1) % Ncc;
6995               ncSet = PETSC_TRUE;
6996             }
6997             if ((cind < fcdof) && (b == fcdofs[cind])) {
6998               ++cind;
6999               fcSet = PETSC_TRUE;
7000             }
7001             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7002           }
7003         } else {
7004           for (b = 0; b < fdof; b++) {
7005             if ((cind < fcdof) && (b == fcdofs[cind])) {
7006               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7007               ++cind;
7008             }
7009           }
7010         }
7011       } else {
7012         if (comps) {
7013           for (b = 0; b < fdof; b++) {
7014             ncSet = fcSet = PETSC_FALSE;
7015             if (b % Nc == comps[ncind]) {
7016               ncind = (ncind + 1) % Ncc;
7017               ncSet = PETSC_TRUE;
7018             }
7019             if ((cind < fcdof) && (b == fcdofs[cind])) {
7020               ++cind;
7021               fcSet = PETSC_TRUE;
7022             }
7023             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7024           }
7025         } else {
7026           for (b = 0; b < fdof; b++) {
7027             if ((cind < fcdof) && (b == fcdofs[cind])) {
7028               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7029               ++cind;
7030             }
7031           }
7032         }
7033       }
7034     } else {
7035       if (perm) {
7036         if (comps) {
7037           for (b = 0; b < fdof; b++) {
7038             ncSet = fcSet = PETSC_FALSE;
7039             if (b % Nc == comps[ncind]) {
7040               ncind = (ncind + 1) % Ncc;
7041               ncSet = PETSC_TRUE;
7042             }
7043             if ((cind < fcdof) && (b == fcdofs[cind])) {
7044               ++cind;
7045               fcSet = PETSC_TRUE;
7046             }
7047             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7048           }
7049         } else {
7050           for (b = 0; b < fdof; b++) {
7051             if ((cind < fcdof) && (b == fcdofs[cind])) {
7052               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7053               ++cind;
7054             }
7055           }
7056         }
7057       } else {
7058         if (comps) {
7059           for (b = 0; b < fdof; b++) {
7060             ncSet = fcSet = PETSC_FALSE;
7061             if (b % Nc == comps[ncind]) {
7062               ncind = (ncind + 1) % Ncc;
7063               ncSet = PETSC_TRUE;
7064             }
7065             if ((cind < fcdof) && (b == fcdofs[cind])) {
7066               ++cind;
7067               fcSet = PETSC_TRUE;
7068             }
7069             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7070           }
7071         } else {
7072           for (b = 0; b < fdof; b++) {
7073             if ((cind < fcdof) && (b == fcdofs[cind])) {
7074               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7075               ++cind;
7076             }
7077           }
7078         }
7079       }
7080     }
7081   }
7082   *offset += fdof;
7083   PetscFunctionReturn(PETSC_SUCCESS);
7084 }
7085 
7086 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7087 {
7088   PetscScalar    *array;
7089   const PetscInt *cone, *coneO;
7090   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7091 
7092   PetscFunctionBeginHot;
7093   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7094   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7095   PetscCall(DMPlexGetCone(dm, point, &cone));
7096   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7097   PetscCall(VecGetArray(v, &array));
7098   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7099     const PetscInt cp = !p ? point : cone[p - 1];
7100     const PetscInt o  = !p ? 0 : coneO[p - 1];
7101 
7102     if ((cp < pStart) || (cp >= pEnd)) {
7103       dof = 0;
7104       continue;
7105     }
7106     PetscCall(PetscSectionGetDof(section, cp, &dof));
7107     /* ADD_VALUES */
7108     {
7109       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7110       PetscScalar    *a;
7111       PetscInt        cdof, coff, cind = 0, k;
7112 
7113       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7114       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7115       a = &array[coff];
7116       if (!cdof) {
7117         if (o >= 0) {
7118           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7119         } else {
7120           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7121         }
7122       } else {
7123         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7124         if (o >= 0) {
7125           for (k = 0; k < dof; ++k) {
7126             if ((cind < cdof) && (k == cdofs[cind])) {
7127               ++cind;
7128               continue;
7129             }
7130             a[k] += values[off + k];
7131           }
7132         } else {
7133           for (k = 0; k < dof; ++k) {
7134             if ((cind < cdof) && (k == cdofs[cind])) {
7135               ++cind;
7136               continue;
7137             }
7138             a[k] += values[off + dof - k - 1];
7139           }
7140         }
7141       }
7142     }
7143   }
7144   PetscCall(VecRestoreArray(v, &array));
7145   PetscFunctionReturn(PETSC_SUCCESS);
7146 }
7147 
7148 /*@C
7149   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7150 
7151   Not collective
7152 
7153   Input Parameters:
7154 + dm      - The `DM`
7155 . section - The section describing the layout in `v`, or `NULL` to use the default section
7156 . v       - The local vector
7157 . point   - The point in the `DM`
7158 . values  - The array of values
7159 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7160             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7161 
7162   Level: intermediate
7163 
7164   Note:
7165   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7166 
7167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7168 @*/
7169 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7170 {
7171   PetscSection    clSection;
7172   IS              clPoints;
7173   PetscScalar    *array;
7174   PetscInt       *points = NULL;
7175   const PetscInt *clp, *clperm = NULL;
7176   PetscInt        depth, numFields, numPoints, p, clsize;
7177 
7178   PetscFunctionBeginHot;
7179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7180   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7181   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7182   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7183   PetscCall(DMPlexGetDepth(dm, &depth));
7184   PetscCall(PetscSectionGetNumFields(section, &numFields));
7185   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7186     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7187     PetscFunctionReturn(PETSC_SUCCESS);
7188   }
7189   /* Get points */
7190   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7191   for (clsize = 0, p = 0; p < numPoints; p++) {
7192     PetscInt dof;
7193     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7194     clsize += dof;
7195   }
7196   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7197   /* Get array */
7198   PetscCall(VecGetArray(v, &array));
7199   /* Get values */
7200   if (numFields > 0) {
7201     PetscInt offset = 0, f;
7202     for (f = 0; f < numFields; ++f) {
7203       const PetscInt    **perms = NULL;
7204       const PetscScalar **flips = NULL;
7205 
7206       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7207       switch (mode) {
7208       case INSERT_VALUES:
7209         for (p = 0; p < numPoints; p++) {
7210           const PetscInt     point = points[2 * p];
7211           const PetscInt    *perm  = perms ? perms[p] : NULL;
7212           const PetscScalar *flip  = flips ? flips[p] : NULL;
7213           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7214         }
7215         break;
7216       case INSERT_ALL_VALUES:
7217         for (p = 0; p < numPoints; p++) {
7218           const PetscInt     point = points[2 * p];
7219           const PetscInt    *perm  = perms ? perms[p] : NULL;
7220           const PetscScalar *flip  = flips ? flips[p] : NULL;
7221           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7222         }
7223         break;
7224       case INSERT_BC_VALUES:
7225         for (p = 0; p < numPoints; p++) {
7226           const PetscInt     point = points[2 * p];
7227           const PetscInt    *perm  = perms ? perms[p] : NULL;
7228           const PetscScalar *flip  = flips ? flips[p] : NULL;
7229           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7230         }
7231         break;
7232       case ADD_VALUES:
7233         for (p = 0; p < numPoints; p++) {
7234           const PetscInt     point = points[2 * p];
7235           const PetscInt    *perm  = perms ? perms[p] : NULL;
7236           const PetscScalar *flip  = flips ? flips[p] : NULL;
7237           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7238         }
7239         break;
7240       case ADD_ALL_VALUES:
7241         for (p = 0; p < numPoints; p++) {
7242           const PetscInt     point = points[2 * p];
7243           const PetscInt    *perm  = perms ? perms[p] : NULL;
7244           const PetscScalar *flip  = flips ? flips[p] : NULL;
7245           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7246         }
7247         break;
7248       case ADD_BC_VALUES:
7249         for (p = 0; p < numPoints; p++) {
7250           const PetscInt     point = points[2 * p];
7251           const PetscInt    *perm  = perms ? perms[p] : NULL;
7252           const PetscScalar *flip  = flips ? flips[p] : NULL;
7253           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7254         }
7255         break;
7256       default:
7257         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7258       }
7259       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7260     }
7261   } else {
7262     PetscInt            dof, off;
7263     const PetscInt    **perms = NULL;
7264     const PetscScalar **flips = NULL;
7265 
7266     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7267     switch (mode) {
7268     case INSERT_VALUES:
7269       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7270         const PetscInt     point = points[2 * p];
7271         const PetscInt    *perm  = perms ? perms[p] : NULL;
7272         const PetscScalar *flip  = flips ? flips[p] : NULL;
7273         PetscCall(PetscSectionGetDof(section, point, &dof));
7274         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7275       }
7276       break;
7277     case INSERT_ALL_VALUES:
7278       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
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(PetscSectionGetDof(section, point, &dof));
7283         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7284       }
7285       break;
7286     case INSERT_BC_VALUES:
7287       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7288         const PetscInt     point = points[2 * p];
7289         const PetscInt    *perm  = perms ? perms[p] : NULL;
7290         const PetscScalar *flip  = flips ? flips[p] : NULL;
7291         PetscCall(PetscSectionGetDof(section, point, &dof));
7292         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7293       }
7294       break;
7295     case ADD_VALUES:
7296       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7297         const PetscInt     point = points[2 * p];
7298         const PetscInt    *perm  = perms ? perms[p] : NULL;
7299         const PetscScalar *flip  = flips ? flips[p] : NULL;
7300         PetscCall(PetscSectionGetDof(section, point, &dof));
7301         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7302       }
7303       break;
7304     case ADD_ALL_VALUES:
7305       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7306         const PetscInt     point = points[2 * p];
7307         const PetscInt    *perm  = perms ? perms[p] : NULL;
7308         const PetscScalar *flip  = flips ? flips[p] : NULL;
7309         PetscCall(PetscSectionGetDof(section, point, &dof));
7310         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7311       }
7312       break;
7313     case ADD_BC_VALUES:
7314       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7315         const PetscInt     point = points[2 * p];
7316         const PetscInt    *perm  = perms ? perms[p] : NULL;
7317         const PetscScalar *flip  = flips ? flips[p] : NULL;
7318         PetscCall(PetscSectionGetDof(section, point, &dof));
7319         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7320       }
7321       break;
7322     default:
7323       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7324     }
7325     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7326   }
7327   /* Cleanup points */
7328   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7329   /* Cleanup array */
7330   PetscCall(VecRestoreArray(v, &array));
7331   PetscFunctionReturn(PETSC_SUCCESS);
7332 }
7333 
7334 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7335 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7336 {
7337   PetscFunctionBegin;
7338   *contains = PETSC_TRUE;
7339   if (label) {
7340     PetscInt fdof;
7341 
7342     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7343     if (!*contains) {
7344       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7345       *offset += fdof;
7346       PetscFunctionReturn(PETSC_SUCCESS);
7347     }
7348   }
7349   PetscFunctionReturn(PETSC_SUCCESS);
7350 }
7351 
7352 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7353 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)
7354 {
7355   PetscSection    clSection;
7356   IS              clPoints;
7357   PetscScalar    *array;
7358   PetscInt       *points = NULL;
7359   const PetscInt *clp;
7360   PetscInt        numFields, numPoints, p;
7361   PetscInt        offset = 0, f;
7362 
7363   PetscFunctionBeginHot;
7364   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7365   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7366   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7367   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7368   PetscCall(PetscSectionGetNumFields(section, &numFields));
7369   /* Get points */
7370   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7371   /* Get array */
7372   PetscCall(VecGetArray(v, &array));
7373   /* Get values */
7374   for (f = 0; f < numFields; ++f) {
7375     const PetscInt    **perms = NULL;
7376     const PetscScalar **flips = NULL;
7377     PetscBool           contains;
7378 
7379     if (!fieldActive[f]) {
7380       for (p = 0; p < numPoints * 2; p += 2) {
7381         PetscInt fdof;
7382         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7383         offset += fdof;
7384       }
7385       continue;
7386     }
7387     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7388     switch (mode) {
7389     case INSERT_VALUES:
7390       for (p = 0; p < numPoints; p++) {
7391         const PetscInt     point = points[2 * p];
7392         const PetscInt    *perm  = perms ? perms[p] : NULL;
7393         const PetscScalar *flip  = flips ? flips[p] : NULL;
7394         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7395         if (!contains) continue;
7396         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7397       }
7398       break;
7399     case INSERT_ALL_VALUES:
7400       for (p = 0; p < numPoints; p++) {
7401         const PetscInt     point = points[2 * p];
7402         const PetscInt    *perm  = perms ? perms[p] : NULL;
7403         const PetscScalar *flip  = flips ? flips[p] : NULL;
7404         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7405         if (!contains) continue;
7406         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7407       }
7408       break;
7409     case INSERT_BC_VALUES:
7410       for (p = 0; p < numPoints; p++) {
7411         const PetscInt     point = points[2 * p];
7412         const PetscInt    *perm  = perms ? perms[p] : NULL;
7413         const PetscScalar *flip  = flips ? flips[p] : NULL;
7414         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7415         if (!contains) continue;
7416         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7417       }
7418       break;
7419     case ADD_VALUES:
7420       for (p = 0; p < numPoints; p++) {
7421         const PetscInt     point = points[2 * p];
7422         const PetscInt    *perm  = perms ? perms[p] : NULL;
7423         const PetscScalar *flip  = flips ? flips[p] : NULL;
7424         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7425         if (!contains) continue;
7426         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7427       }
7428       break;
7429     case ADD_ALL_VALUES:
7430       for (p = 0; p < numPoints; p++) {
7431         const PetscInt     point = points[2 * p];
7432         const PetscInt    *perm  = perms ? perms[p] : NULL;
7433         const PetscScalar *flip  = flips ? flips[p] : NULL;
7434         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7435         if (!contains) continue;
7436         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7437       }
7438       break;
7439     default:
7440       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7441     }
7442     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7443   }
7444   /* Cleanup points */
7445   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7446   /* Cleanup array */
7447   PetscCall(VecRestoreArray(v, &array));
7448   PetscFunctionReturn(PETSC_SUCCESS);
7449 }
7450 
7451 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7452 {
7453   PetscMPIInt rank;
7454   PetscInt    i, j;
7455 
7456   PetscFunctionBegin;
7457   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7458   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7459   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7460   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7461   numCIndices = numCIndices ? numCIndices : numRIndices;
7462   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7463   for (i = 0; i < numRIndices; i++) {
7464     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7465     for (j = 0; j < numCIndices; j++) {
7466 #if defined(PETSC_USE_COMPLEX)
7467       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7468 #else
7469       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7470 #endif
7471     }
7472     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7473   }
7474   PetscFunctionReturn(PETSC_SUCCESS);
7475 }
7476 
7477 /*
7478   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7479 
7480   Input Parameters:
7481 + section - The section for this data layout
7482 . islocal - Is the section (and thus indices being requested) local or global?
7483 . point   - The point contributing dofs with these indices
7484 . off     - The global offset of this point
7485 . loff    - The local offset of each field
7486 . setBC   - The flag determining whether to include indices of boundary values
7487 . perm    - A permutation of the dofs on this point, or NULL
7488 - indperm - A permutation of the entire indices array, or NULL
7489 
7490   Output Parameter:
7491 . indices - Indices for dofs on this point
7492 
7493   Level: developer
7494 
7495   Note: The indices could be local or global, depending on the value of 'off'.
7496 */
7497 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7498 {
7499   PetscInt        dof;   /* The number of unknowns on this point */
7500   PetscInt        cdof;  /* The number of constraints on this point */
7501   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7502   PetscInt        cind = 0, k;
7503 
7504   PetscFunctionBegin;
7505   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7506   PetscCall(PetscSectionGetDof(section, point, &dof));
7507   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7508   if (!cdof || setBC) {
7509     for (k = 0; k < dof; ++k) {
7510       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7511       const PetscInt ind    = indperm ? indperm[preind] : preind;
7512 
7513       indices[ind] = off + k;
7514     }
7515   } else {
7516     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7517     for (k = 0; k < dof; ++k) {
7518       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7519       const PetscInt ind    = indperm ? indperm[preind] : preind;
7520 
7521       if ((cind < cdof) && (k == cdofs[cind])) {
7522         /* Insert check for returning constrained indices */
7523         indices[ind] = -(off + k + 1);
7524         ++cind;
7525       } else {
7526         indices[ind] = off + k - (islocal ? 0 : cind);
7527       }
7528     }
7529   }
7530   *loff += dof;
7531   PetscFunctionReturn(PETSC_SUCCESS);
7532 }
7533 
7534 /*
7535  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7536 
7537  Input Parameters:
7538 + section - a section (global or local)
7539 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7540 . point - point within section
7541 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7542 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7543 . setBC - identify constrained (boundary condition) points via involution.
7544 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7545 . permsoff - offset
7546 - indperm - index permutation
7547 
7548  Output Parameter:
7549 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7550 . indices - array to hold indices (as defined by section) of each dof associated with point
7551 
7552  Notes:
7553  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7554  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7555  in the local vector.
7556 
7557  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7558  significant).  It is invalid to call with a global section and setBC=true.
7559 
7560  Developer Note:
7561  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7562  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7563  offset could be obtained from the section instead of passing it explicitly as we do now.
7564 
7565  Example:
7566  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7567  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7568  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7569  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.
7570 
7571  Level: developer
7572 */
7573 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[])
7574 {
7575   PetscInt numFields, foff, f;
7576 
7577   PetscFunctionBegin;
7578   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7579   PetscCall(PetscSectionGetNumFields(section, &numFields));
7580   for (f = 0, foff = 0; f < numFields; ++f) {
7581     PetscInt        fdof, cfdof;
7582     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7583     PetscInt        cind = 0, b;
7584     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7585 
7586     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7587     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7588     if (!cfdof || setBC) {
7589       for (b = 0; b < fdof; ++b) {
7590         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7591         const PetscInt ind    = indperm ? indperm[preind] : preind;
7592 
7593         indices[ind] = off + foff + b;
7594       }
7595     } else {
7596       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7597       for (b = 0; b < fdof; ++b) {
7598         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7599         const PetscInt ind    = indperm ? indperm[preind] : preind;
7600 
7601         if ((cind < cfdof) && (b == fcdofs[cind])) {
7602           indices[ind] = -(off + foff + b + 1);
7603           ++cind;
7604         } else {
7605           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7606         }
7607       }
7608     }
7609     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7610     foffs[f] += fdof;
7611   }
7612   PetscFunctionReturn(PETSC_SUCCESS);
7613 }
7614 
7615 /*
7616   This version believes the globalSection offsets for each field, rather than just the point offset
7617 
7618  . foffs - The offset into 'indices' for each field, since it is segregated by field
7619 
7620  Notes:
7621  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7622  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7623 */
7624 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7625 {
7626   PetscInt numFields, foff, f;
7627 
7628   PetscFunctionBegin;
7629   PetscCall(PetscSectionGetNumFields(section, &numFields));
7630   for (f = 0; f < numFields; ++f) {
7631     PetscInt        fdof, cfdof;
7632     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7633     PetscInt        cind = 0, b;
7634     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7635 
7636     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7637     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7638     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7639     if (!cfdof) {
7640       for (b = 0; b < fdof; ++b) {
7641         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7642         const PetscInt ind    = indperm ? indperm[preind] : preind;
7643 
7644         indices[ind] = foff + b;
7645       }
7646     } else {
7647       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7648       for (b = 0; b < fdof; ++b) {
7649         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7650         const PetscInt ind    = indperm ? indperm[preind] : preind;
7651 
7652         if ((cind < cfdof) && (b == fcdofs[cind])) {
7653           indices[ind] = -(foff + b + 1);
7654           ++cind;
7655         } else {
7656           indices[ind] = foff + b - cind;
7657         }
7658       }
7659     }
7660     foffs[f] += fdof;
7661   }
7662   PetscFunctionReturn(PETSC_SUCCESS);
7663 }
7664 
7665 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7666 {
7667   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7668 
7669   PetscFunctionBegin;
7670   PetscCall(PetscSectionGetNumFields(section, &numFields));
7671   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7672   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7673   for (PetscInt p = 0; p < nPoints; p++) {
7674     PetscInt     b       = pnts[2 * p];
7675     PetscInt     bSecDof = 0, bOff;
7676     PetscInt     cSecDof = 0;
7677     PetscSection indices_section;
7678 
7679     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7680     if (!bSecDof) continue;
7681     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7682     indices_section = cSecDof > 0 ? cSec : section;
7683     if (numFields) {
7684       PetscInt fStart[32], fEnd[32];
7685 
7686       fStart[0] = 0;
7687       fEnd[0]   = 0;
7688       for (PetscInt f = 0; f < numFields; f++) {
7689         PetscInt fDof = 0;
7690 
7691         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7692         fStart[f + 1] = fStart[f] + fDof;
7693         fEnd[f + 1]   = fStart[f + 1];
7694       }
7695       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7696       // only apply permutations on one side
7697       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7698       for (PetscInt f = 0; f < numFields; f++) {
7699         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7700       }
7701     } else {
7702       PetscInt bEnd = 0;
7703 
7704       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7705       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7706 
7707       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7708     }
7709   }
7710   PetscFunctionReturn(PETSC_SUCCESS);
7711 }
7712 
7713 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[])
7714 {
7715   Mat             cMat;
7716   PetscSection    aSec, cSec;
7717   IS              aIS;
7718   PetscInt        aStart = -1, aEnd = -1;
7719   PetscInt        sStart = -1, sEnd = -1;
7720   PetscInt        cStart = -1, cEnd = -1;
7721   const PetscInt *anchors;
7722   PetscInt        numFields, p;
7723   PetscInt        newNumPoints = 0, newNumIndices = 0;
7724   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7725   PetscInt        oldOffsets[32];
7726   PetscInt        newOffsets[32];
7727   PetscInt        oldOffsetsCopy[32];
7728   PetscInt        newOffsetsCopy[32];
7729   PetscScalar    *modMat         = NULL;
7730   PetscBool       anyConstrained = PETSC_FALSE;
7731 
7732   PetscFunctionBegin;
7733   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7734   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7735   PetscCall(PetscSectionGetNumFields(section, &numFields));
7736 
7737   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7738   /* if there are point-to-point constraints */
7739   if (aSec) {
7740     PetscCall(PetscArrayzero(newOffsets, 32));
7741     PetscCall(PetscArrayzero(oldOffsets, 32));
7742     PetscCall(ISGetIndices(aIS, &anchors));
7743     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7744     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7745     /* figure out how many points are going to be in the new element matrix
7746      * (we allow double counting, because it's all just going to be summed
7747      * into the global matrix anyway) */
7748     for (p = 0; p < 2 * numPoints; p += 2) {
7749       PetscInt b    = points[p];
7750       PetscInt bDof = 0, bSecDof = 0;
7751 
7752       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7753       if (!bSecDof) continue;
7754 
7755       for (PetscInt f = 0; f < numFields; f++) {
7756         PetscInt fDof = 0;
7757 
7758         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7759         oldOffsets[f + 1] += fDof;
7760       }
7761       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7762       if (bDof) {
7763         /* this point is constrained */
7764         /* it is going to be replaced by its anchors */
7765         PetscInt bOff, q;
7766 
7767         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7768         for (q = 0; q < bDof; q++) {
7769           PetscInt a    = anchors[bOff + q];
7770           PetscInt aDof = 0;
7771 
7772           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7773           if (aDof) {
7774             anyConstrained = PETSC_TRUE;
7775             newNumPoints += 1;
7776           }
7777           newNumIndices += aDof;
7778           for (PetscInt f = 0; f < numFields; ++f) {
7779             PetscInt fDof = 0;
7780 
7781             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7782             newOffsets[f + 1] += fDof;
7783           }
7784         }
7785       } else {
7786         /* this point is not constrained */
7787         newNumPoints++;
7788         newNumIndices += bSecDof;
7789         for (PetscInt f = 0; f < numFields; ++f) {
7790           PetscInt fDof;
7791 
7792           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7793           newOffsets[f + 1] += fDof;
7794         }
7795       }
7796     }
7797   }
7798   if (!anyConstrained) {
7799     if (outNumPoints) *outNumPoints = 0;
7800     if (outNumIndices) *outNumIndices = 0;
7801     if (outPoints) *outPoints = NULL;
7802     if (outMat) *outMat = NULL;
7803     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7804     PetscFunctionReturn(PETSC_SUCCESS);
7805   }
7806 
7807   if (outNumPoints) *outNumPoints = newNumPoints;
7808   if (outNumIndices) *outNumIndices = newNumIndices;
7809 
7810   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7811   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7812 
7813   if (!outPoints && !outMat) {
7814     if (offsets) {
7815       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7816     }
7817     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7818     PetscFunctionReturn(PETSC_SUCCESS);
7819   }
7820 
7821   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7822   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7823 
7824   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7825   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7826 
7827   /* output arrays */
7828   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7829   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7830 
7831   // get the new Points
7832   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7833     PetscInt b    = points[2 * p];
7834     PetscInt bDof = 0, bSecDof = 0, bOff;
7835 
7836     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7837     if (!bSecDof) continue;
7838     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7839     if (bDof) {
7840       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7841       for (PetscInt q = 0; q < bDof; q++) {
7842         PetscInt a = anchors[bOff + q], aDof = 0;
7843 
7844         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7845         if (aDof) {
7846           newPoints[2 * newP]     = a;
7847           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7848           newP++;
7849         }
7850       }
7851     } else {
7852       newPoints[2 * newP]     = b;
7853       newPoints[2 * newP + 1] = points[2 * p + 1];
7854       newP++;
7855     }
7856   }
7857 
7858   if (outMat) {
7859     PetscScalar *tmpMat;
7860     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7861     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7862 
7863     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7864     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7865     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7866     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7867 
7868     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7869     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7870 
7871     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7872     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7873 
7874     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7875     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7876     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7877     // for each field, insert the anchor modification into modMat
7878     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7879       PetscInt fStart    = oldOffsets[f];
7880       PetscInt fNewStart = newOffsets[f];
7881       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7882         PetscInt b    = points[2 * p];
7883         PetscInt bDof = 0, bSecDof = 0, bOff;
7884 
7885         if (b >= sStart && b < sEnd) {
7886           if (numFields) {
7887             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7888           } else {
7889             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7890           }
7891         }
7892         if (!bSecDof) continue;
7893         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7894         if (bDof) {
7895           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7896           for (PetscInt q = 0; q < bDof; q++, newP++) {
7897             PetscInt a = anchors[bOff + q], aDof = 0;
7898 
7899             if (a >= sStart && a < sEnd) {
7900               if (numFields) {
7901                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7902               } else {
7903                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7904               }
7905             }
7906             if (aDof) {
7907               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7908               for (PetscInt d = 0; d < bSecDof; d++) {
7909                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7910               }
7911             }
7912             oNew += aDof;
7913           }
7914         } else {
7915           // Insert the identity matrix in this block
7916           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7917           oNew += bSecDof;
7918           newP++;
7919         }
7920         o += bSecDof;
7921       }
7922     }
7923 
7924     *outMat = modMat;
7925 
7926     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7927     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7928     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7929     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7930     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7931   }
7932   PetscCall(ISRestoreIndices(aIS, &anchors));
7933 
7934   /* output */
7935   if (outPoints) {
7936     *outPoints = newPoints;
7937   } else {
7938     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7939   }
7940   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7941   PetscFunctionReturn(PETSC_SUCCESS);
7942 }
7943 
7944 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)
7945 {
7946   PetscScalar *modMat        = NULL;
7947   PetscInt     newNumIndices = -1;
7948 
7949   PetscFunctionBegin;
7950   /* 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.
7951      modMat is that matrix C */
7952   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7953   if (outNumIndices) *outNumIndices = newNumIndices;
7954   if (modMat) {
7955     const PetscScalar *newValues = values;
7956 
7957     if (multiplyRight) {
7958       PetscScalar *newNewValues = NULL;
7959       PetscBLASInt M, N, K;
7960       PetscScalar  a = 1.0, b = 0.0;
7961 
7962       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);
7963 
7964       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7965       PetscCall(PetscBLASIntCast(numRows, &N));
7966       PetscCall(PetscBLASIntCast(numIndices, &K));
7967       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7968       // row-major to column-major conversion, right multiplication becomes left multiplication
7969       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7970       numCols   = newNumIndices;
7971       newValues = newNewValues;
7972     }
7973 
7974     if (multiplyLeft) {
7975       PetscScalar *newNewValues = NULL;
7976       PetscBLASInt M, N, K;
7977       PetscScalar  a = 1.0, b = 0.0;
7978 
7979       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);
7980 
7981       PetscCall(PetscBLASIntCast(numCols, &M));
7982       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7983       PetscCall(PetscBLASIntCast(numIndices, &K));
7984       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7985       // row-major to column-major conversion, left multiplication becomes right multiplication
7986       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7987       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7988       newValues = newNewValues;
7989     }
7990     *outValues = (PetscScalar *)newValues;
7991     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7992   }
7993   PetscFunctionReturn(PETSC_SUCCESS);
7994 }
7995 
7996 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)
7997 {
7998   PetscFunctionBegin;
7999   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8000   PetscFunctionReturn(PETSC_SUCCESS);
8001 }
8002 
8003 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8004 {
8005   /* Closure ordering */
8006   PetscSection    clSection;
8007   IS              clPoints;
8008   const PetscInt *clp;
8009   PetscInt       *points;
8010   PetscInt        Ncl, Ni = 0;
8011 
8012   PetscFunctionBeginHot;
8013   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8014   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8015     PetscInt dof;
8016 
8017     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8018     Ni += dof;
8019   }
8020   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8021   *closureSize = Ni;
8022   PetscFunctionReturn(PETSC_SUCCESS);
8023 }
8024 
8025 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)
8026 {
8027   /* Closure ordering */
8028   PetscSection    clSection;
8029   IS              clPoints;
8030   const PetscInt *clp;
8031   PetscInt       *points;
8032   const PetscInt *clperm = NULL;
8033   /* Dof permutation and sign flips */
8034   const PetscInt    **perms[32] = {NULL};
8035   const PetscScalar **flips[32] = {NULL};
8036   PetscScalar        *valCopy   = NULL;
8037   /* Hanging node constraints */
8038   PetscInt    *pointsC = NULL;
8039   PetscScalar *valuesC = NULL;
8040   PetscInt     NclC, NiC;
8041 
8042   PetscInt *idx;
8043   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8044   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8045   PetscInt  idxStart, idxEnd;
8046   PetscInt  nRows, nCols;
8047 
8048   PetscFunctionBeginHot;
8049   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8050   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8051   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8052   PetscAssertPointer(numRows, 6);
8053   PetscAssertPointer(numCols, 7);
8054   if (indices) PetscAssertPointer(indices, 8);
8055   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8056   if (values) PetscAssertPointer(values, 10);
8057   PetscCall(PetscSectionGetNumFields(section, &Nf));
8058   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8059   PetscCall(PetscArrayzero(offsets, 32));
8060   /* 1) Get points in closure */
8061   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8062   if (useClPerm) {
8063     PetscInt depth, clsize;
8064     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8065     for (clsize = 0, p = 0; p < Ncl; p++) {
8066       PetscInt dof;
8067       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8068       clsize += dof;
8069     }
8070     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8071   }
8072   /* 2) Get number of indices on these points and field offsets from section */
8073   for (p = 0; p < Ncl * 2; p += 2) {
8074     PetscInt dof, fdof;
8075 
8076     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8077     for (f = 0; f < Nf; ++f) {
8078       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8079       offsets[f + 1] += fdof;
8080     }
8081     Ni += dof;
8082   }
8083   if (*numRows == -1) *numRows = Ni;
8084   if (*numCols == -1) *numCols = Ni;
8085   nRows = *numRows;
8086   nCols = *numCols;
8087   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8088   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8089   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8090   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8091   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8092   for (f = 0; f < PetscMax(1, Nf); ++f) {
8093     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8094     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8095     /* may need to apply sign changes to the element matrix */
8096     if (values && flips[f]) {
8097       PetscInt foffset = offsets[f];
8098 
8099       for (p = 0; p < Ncl; ++p) {
8100         PetscInt           pnt  = points[2 * p], fdof;
8101         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8102 
8103         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8104         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8105         if (flip) {
8106           PetscInt i, j, k;
8107 
8108           if (!valCopy) {
8109             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8110             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8111             *values = valCopy;
8112           }
8113           for (i = 0; i < fdof; ++i) {
8114             PetscScalar fval = flip[i];
8115 
8116             if (multiplyRight) {
8117               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8118             }
8119             if (multiplyLeft) {
8120               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8121             }
8122           }
8123         }
8124         foffset += fdof;
8125       }
8126     }
8127   }
8128   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8129   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8130   if (NclC) {
8131     if (multiplyRight) *numCols = NiC;
8132     if (multiplyLeft) *numRows = NiC;
8133     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8134     for (f = 0; f < PetscMax(1, Nf); ++f) {
8135       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8136       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8137     }
8138     for (f = 0; f < PetscMax(1, Nf); ++f) {
8139       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8140       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8141     }
8142     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8143     Ncl    = NclC;
8144     Ni     = NiC;
8145     points = pointsC;
8146     if (values) *values = valuesC;
8147   }
8148   /* 5) Calculate indices */
8149   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8150   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8151   if (Nf) {
8152     PetscInt  idxOff;
8153     PetscBool useFieldOffsets;
8154 
8155     if (outOffsets) {
8156       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8157     }
8158     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8159     if (useFieldOffsets) {
8160       for (p = 0; p < Ncl; ++p) {
8161         const PetscInt pnt = points[p * 2];
8162 
8163         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8164       }
8165     } else {
8166       for (p = 0; p < Ncl; ++p) {
8167         const PetscInt pnt = points[p * 2];
8168 
8169         if (pnt < idxStart || pnt >= idxEnd) continue;
8170         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8171         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8172          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8173          * global section. */
8174         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8175       }
8176     }
8177   } else {
8178     PetscInt off = 0, idxOff;
8179 
8180     for (p = 0; p < Ncl; ++p) {
8181       const PetscInt  pnt  = points[p * 2];
8182       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8183 
8184       if (pnt < idxStart || pnt >= idxEnd) continue;
8185       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8186       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8187        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8188       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8189     }
8190   }
8191   /* 6) Cleanup */
8192   for (f = 0; f < PetscMax(1, Nf); ++f) {
8193     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8194     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8195   }
8196   if (NclC) {
8197     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8198   } else {
8199     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8200   }
8201 
8202   if (indices) *indices = idx;
8203   PetscFunctionReturn(PETSC_SUCCESS);
8204 }
8205 
8206 /*@C
8207   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8208 
8209   Not collective
8210 
8211   Input Parameters:
8212 + dm         - The `DM`
8213 . section    - The `PetscSection` describing the points (a local section)
8214 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8215 . point      - The point defining the closure
8216 - useClPerm  - Use the closure point permutation if available
8217 
8218   Output Parameters:
8219 + numIndices - The number of dof indices in the closure of point with the input sections
8220 . indices    - The dof indices
8221 . outOffsets - Array to write the field offsets into, or `NULL`
8222 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8223 
8224   Level: advanced
8225 
8226   Notes:
8227   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8228 
8229   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8230   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8231   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8232   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8233   indices (with the above semantics) are implied.
8234 
8235 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8236           `PetscSection`, `DMGetGlobalSection()`
8237 @*/
8238 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8239 {
8240   PetscInt numRows = -1, numCols = -1;
8241 
8242   PetscFunctionBeginHot;
8243   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8244   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8245   *numIndices = numRows;
8246   PetscFunctionReturn(PETSC_SUCCESS);
8247 }
8248 
8249 /*@C
8250   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8251 
8252   Not collective
8253 
8254   Input Parameters:
8255 + dm         - The `DM`
8256 . section    - The `PetscSection` describing the points (a local section)
8257 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8258 . point      - The point defining the closure
8259 - useClPerm  - Use the closure point permutation if available
8260 
8261   Output Parameters:
8262 + numIndices - The number of dof indices in the closure of point with the input sections
8263 . indices    - The dof indices
8264 . outOffsets - Array to write the field offsets into, or `NULL`
8265 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8266 
8267   Level: advanced
8268 
8269   Notes:
8270   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8271 
8272   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8273   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8274   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8275   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8276   indices (with the above semantics) are implied.
8277 
8278 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8279 @*/
8280 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8281 {
8282   PetscFunctionBegin;
8283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8284   PetscAssertPointer(indices, 7);
8285   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8286   PetscFunctionReturn(PETSC_SUCCESS);
8287 }
8288 
8289 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8290 {
8291   DM_Plex           *mesh = (DM_Plex *)dm->data;
8292   PetscInt          *indices;
8293   PetscInt           numIndices;
8294   const PetscScalar *valuesOrig = values;
8295   PetscErrorCode     ierr;
8296 
8297   PetscFunctionBegin;
8298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8299   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8300   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8301   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8302   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8303   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8304 
8305   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8306 
8307   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8308   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8309   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8310   if (ierr) {
8311     PetscMPIInt rank;
8312 
8313     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8314     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8315     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8316     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8317     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8318     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8319   }
8320   if (mesh->printFEM > 1) {
8321     PetscInt i;
8322     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8323     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8324     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8325   }
8326 
8327   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8328   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8329   PetscFunctionReturn(PETSC_SUCCESS);
8330 }
8331 
8332 /*@C
8333   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8334 
8335   Not collective
8336 
8337   Input Parameters:
8338 + dm            - The `DM`
8339 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8340 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8341 . A             - The matrix
8342 . point         - The point in the `DM`
8343 . values        - The array of values
8344 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8345 
8346   Level: intermediate
8347 
8348 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8349 @*/
8350 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8351 {
8352   PetscFunctionBegin;
8353   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8354   PetscFunctionReturn(PETSC_SUCCESS);
8355 }
8356 
8357 /*@C
8358   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8359 
8360   Not collective
8361 
8362   Input Parameters:
8363 + dmRow            - The `DM` for the row fields
8364 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8365 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8366 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8367 . dmCol            - The `DM` for the column fields
8368 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8369 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8370 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8371 . A                - The matrix
8372 . point            - The point in the `DM`
8373 . values           - The array of values
8374 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8375 
8376   Level: intermediate
8377 
8378 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8379 @*/
8380 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)
8381 {
8382   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8383   PetscInt          *indicesRow, *indicesCol;
8384   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8385   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8386 
8387   PetscErrorCode ierr;
8388 
8389   PetscFunctionBegin;
8390   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8391   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8392   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8393   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8394   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8395   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8396   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8397   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8398   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8399   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8400   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8401 
8402   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8403   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8404   valuesV1 = valuesV0;
8405   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8406   valuesV2 = valuesV1;
8407   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8408 
8409   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8410   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8411   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8412   if (ierr) {
8413     PetscMPIInt rank;
8414 
8415     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8416     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8417     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8418     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8419     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8420     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8421     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8422   }
8423 
8424   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8425   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8426   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8427   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8428   PetscFunctionReturn(PETSC_SUCCESS);
8429 }
8430 
8431 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8432 {
8433   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8434   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8435   PetscInt       *cpoints = NULL;
8436   PetscInt       *findices, *cindices;
8437   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8438   PetscInt        foffsets[32], coffsets[32];
8439   DMPolytopeType  ct;
8440   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8441   PetscErrorCode  ierr;
8442 
8443   PetscFunctionBegin;
8444   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8445   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8446   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8447   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8448   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8449   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8450   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8451   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8452   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8453   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8454   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8455   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8456   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8457   PetscCall(PetscArrayzero(foffsets, 32));
8458   PetscCall(PetscArrayzero(coffsets, 32));
8459   /* Column indices */
8460   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8461   maxFPoints = numCPoints;
8462   /* Compress out points not in the section */
8463   /*   TODO: Squeeze out points with 0 dof as well */
8464   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8465   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8466     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8467       cpoints[q * 2]     = cpoints[p];
8468       cpoints[q * 2 + 1] = cpoints[p + 1];
8469       ++q;
8470     }
8471   }
8472   numCPoints = q;
8473   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8474     PetscInt fdof;
8475 
8476     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8477     if (!dof) continue;
8478     for (f = 0; f < numFields; ++f) {
8479       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8480       coffsets[f + 1] += fdof;
8481     }
8482     numCIndices += dof;
8483   }
8484   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8485   /* Row indices */
8486   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8487   {
8488     DMPlexTransform tr;
8489     DMPolytopeType *rct;
8490     PetscInt       *rsize, *rcone, *rornt, Nt;
8491 
8492     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8493     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8494     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8495     numSubcells = rsize[Nt - 1];
8496     PetscCall(DMPlexTransformDestroy(&tr));
8497   }
8498   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8499   for (r = 0, q = 0; r < numSubcells; ++r) {
8500     /* TODO Map from coarse to fine cells */
8501     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8502     /* Compress out points not in the section */
8503     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8504     for (p = 0; p < numFPoints * 2; p += 2) {
8505       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8506         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8507         if (!dof) continue;
8508         for (s = 0; s < q; ++s)
8509           if (fpoints[p] == ftotpoints[s * 2]) break;
8510         if (s < q) continue;
8511         ftotpoints[q * 2]     = fpoints[p];
8512         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8513         ++q;
8514       }
8515     }
8516     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8517   }
8518   numFPoints = q;
8519   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8520     PetscInt fdof;
8521 
8522     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8523     if (!dof) continue;
8524     for (f = 0; f < numFields; ++f) {
8525       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8526       foffsets[f + 1] += fdof;
8527     }
8528     numFIndices += dof;
8529   }
8530   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8531 
8532   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8533   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8534   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8535   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8536   if (numFields) {
8537     const PetscInt **permsF[32] = {NULL};
8538     const PetscInt **permsC[32] = {NULL};
8539 
8540     for (f = 0; f < numFields; f++) {
8541       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8542       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8543     }
8544     for (p = 0; p < numFPoints; p++) {
8545       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8546       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8547     }
8548     for (p = 0; p < numCPoints; p++) {
8549       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8550       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8551     }
8552     for (f = 0; f < numFields; f++) {
8553       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8554       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8555     }
8556   } else {
8557     const PetscInt **permsF = NULL;
8558     const PetscInt **permsC = NULL;
8559 
8560     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8561     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8562     for (p = 0, off = 0; p < numFPoints; p++) {
8563       const PetscInt *perm = permsF ? permsF[p] : NULL;
8564 
8565       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8566       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8567     }
8568     for (p = 0, off = 0; p < numCPoints; p++) {
8569       const PetscInt *perm = permsC ? permsC[p] : NULL;
8570 
8571       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8572       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8573     }
8574     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8575     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8576   }
8577   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8578   /* TODO: flips */
8579   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8580   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8581   if (ierr) {
8582     PetscMPIInt rank;
8583 
8584     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8585     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8586     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8587     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8588     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8589   }
8590   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8591   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8592   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8593   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8594   PetscFunctionReturn(PETSC_SUCCESS);
8595 }
8596 
8597 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8598 {
8599   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8600   PetscInt       *cpoints      = NULL;
8601   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8602   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8603   DMPolytopeType  ct;
8604   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8605 
8606   PetscFunctionBegin;
8607   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8608   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8609   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8610   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8611   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8612   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8613   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8614   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8615   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8616   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8617   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8618   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8619   /* Column indices */
8620   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8621   maxFPoints = numCPoints;
8622   /* Compress out points not in the section */
8623   /*   TODO: Squeeze out points with 0 dof as well */
8624   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8625   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8626     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8627       cpoints[q * 2]     = cpoints[p];
8628       cpoints[q * 2 + 1] = cpoints[p + 1];
8629       ++q;
8630     }
8631   }
8632   numCPoints = q;
8633   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8634     PetscInt fdof;
8635 
8636     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8637     if (!dof) continue;
8638     for (f = 0; f < numFields; ++f) {
8639       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8640       coffsets[f + 1] += fdof;
8641     }
8642     numCIndices += dof;
8643   }
8644   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8645   /* Row indices */
8646   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8647   {
8648     DMPlexTransform tr;
8649     DMPolytopeType *rct;
8650     PetscInt       *rsize, *rcone, *rornt, Nt;
8651 
8652     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8653     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8654     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8655     numSubcells = rsize[Nt - 1];
8656     PetscCall(DMPlexTransformDestroy(&tr));
8657   }
8658   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8659   for (r = 0, q = 0; r < numSubcells; ++r) {
8660     /* TODO Map from coarse to fine cells */
8661     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8662     /* Compress out points not in the section */
8663     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8664     for (p = 0; p < numFPoints * 2; p += 2) {
8665       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8666         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8667         if (!dof) continue;
8668         for (s = 0; s < q; ++s)
8669           if (fpoints[p] == ftotpoints[s * 2]) break;
8670         if (s < q) continue;
8671         ftotpoints[q * 2]     = fpoints[p];
8672         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8673         ++q;
8674       }
8675     }
8676     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8677   }
8678   numFPoints = q;
8679   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8680     PetscInt fdof;
8681 
8682     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8683     if (!dof) continue;
8684     for (f = 0; f < numFields; ++f) {
8685       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8686       foffsets[f + 1] += fdof;
8687     }
8688     numFIndices += dof;
8689   }
8690   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8691 
8692   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8693   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8694   if (numFields) {
8695     const PetscInt **permsF[32] = {NULL};
8696     const PetscInt **permsC[32] = {NULL};
8697 
8698     for (f = 0; f < numFields; f++) {
8699       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8700       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8701     }
8702     for (p = 0; p < numFPoints; p++) {
8703       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8704       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8705     }
8706     for (p = 0; p < numCPoints; p++) {
8707       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8708       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8709     }
8710     for (f = 0; f < numFields; f++) {
8711       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8712       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8713     }
8714   } else {
8715     const PetscInt **permsF = NULL;
8716     const PetscInt **permsC = NULL;
8717 
8718     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8719     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8720     for (p = 0, off = 0; p < numFPoints; p++) {
8721       const PetscInt *perm = permsF ? permsF[p] : NULL;
8722 
8723       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8724       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8725     }
8726     for (p = 0, off = 0; p < numCPoints; p++) {
8727       const PetscInt *perm = permsC ? permsC[p] : NULL;
8728 
8729       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8730       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8731     }
8732     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8733     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8734   }
8735   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8736   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8737   PetscFunctionReturn(PETSC_SUCCESS);
8738 }
8739 
8740 /*@
8741   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8742 
8743   Input Parameter:
8744 . dm - The `DMPLEX` object
8745 
8746   Output Parameter:
8747 . cellHeight - The height of a cell
8748 
8749   Level: developer
8750 
8751 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8752 @*/
8753 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8754 {
8755   DM_Plex *mesh = (DM_Plex *)dm->data;
8756 
8757   PetscFunctionBegin;
8758   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8759   PetscAssertPointer(cellHeight, 2);
8760   *cellHeight = mesh->vtkCellHeight;
8761   PetscFunctionReturn(PETSC_SUCCESS);
8762 }
8763 
8764 /*@
8765   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8766 
8767   Input Parameters:
8768 + dm         - The `DMPLEX` object
8769 - cellHeight - The height of a cell
8770 
8771   Level: developer
8772 
8773 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8774 @*/
8775 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8776 {
8777   DM_Plex *mesh = (DM_Plex *)dm->data;
8778 
8779   PetscFunctionBegin;
8780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8781   mesh->vtkCellHeight = cellHeight;
8782   PetscFunctionReturn(PETSC_SUCCESS);
8783 }
8784 
8785 /*@
8786   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8787 
8788   Input Parameters:
8789 + dm - The `DMPLEX` object
8790 - ct - The `DMPolytopeType` of the cell
8791 
8792   Output Parameters:
8793 + start - The first cell of this type, or `NULL`
8794 - end   - The upper bound on this celltype, or `NULL`
8795 
8796   Level: advanced
8797 
8798 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8799 @*/
8800 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8801 {
8802   DM_Plex *mesh = (DM_Plex *)dm->data;
8803   DMLabel  label;
8804   PetscInt pStart, pEnd;
8805 
8806   PetscFunctionBegin;
8807   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8808   if (start) {
8809     PetscAssertPointer(start, 3);
8810     *start = 0;
8811   }
8812   if (end) {
8813     PetscAssertPointer(end, 4);
8814     *end = 0;
8815   }
8816   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8817   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8818   if (mesh->tr) {
8819     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8820   } else {
8821     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8822     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8823     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8824   }
8825   PetscFunctionReturn(PETSC_SUCCESS);
8826 }
8827 
8828 /*@
8829   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8830 
8831   Input Parameters:
8832 + dm    - The `DMPLEX` object
8833 - depth - The depth for the given point stratum
8834 
8835   Output Parameter:
8836 . gsize - The global number of points in the stratum
8837 
8838   Level: advanced
8839 
8840 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8841 @*/
8842 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8843 {
8844   PetscSF         sf;
8845   const PetscInt *leaves;
8846   PetscInt        Nl, loc, start, end, lsize = 0;
8847 
8848   PetscFunctionBegin;
8849   PetscCall(DMGetPointSF(dm, &sf));
8850   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8851   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8852   for (PetscInt p = start; p < end; ++p) {
8853     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8854     if (loc < 0) ++lsize;
8855   }
8856   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8857   PetscFunctionReturn(PETSC_SUCCESS);
8858 }
8859 
8860 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8861 {
8862   PetscSection section, globalSection;
8863   PetscInt    *numbers, p;
8864 
8865   PetscFunctionBegin;
8866   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8867   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8868   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8869   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8870   PetscCall(PetscSectionSetUp(section));
8871   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8872   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8873   for (p = pStart; p < pEnd; ++p) {
8874     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8875     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8876     else numbers[p - pStart] += shift;
8877   }
8878   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8879   if (globalSize) {
8880     PetscLayout layout;
8881     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8882     PetscCall(PetscLayoutGetSize(layout, globalSize));
8883     PetscCall(PetscLayoutDestroy(&layout));
8884   }
8885   PetscCall(PetscSectionDestroy(&section));
8886   PetscCall(PetscSectionDestroy(&globalSection));
8887   PetscFunctionReturn(PETSC_SUCCESS);
8888 }
8889 
8890 /*@
8891   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8892 
8893   Input Parameters:
8894 + dm         - The `DMPLEX` object
8895 - includeAll - Whether to include all cells, or just the simplex and box cells
8896 
8897   Output Parameter:
8898 . globalCellNumbers - Global cell numbers for all cells on this process
8899 
8900   Level: developer
8901 
8902 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8903 @*/
8904 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8905 {
8906   PetscInt cellHeight, cStart, cEnd;
8907 
8908   PetscFunctionBegin;
8909   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8910   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8911   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8912   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8913   PetscFunctionReturn(PETSC_SUCCESS);
8914 }
8915 
8916 /*@
8917   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8918 
8919   Input Parameter:
8920 . dm - The `DMPLEX` object
8921 
8922   Output Parameter:
8923 . globalCellNumbers - Global cell numbers for all cells on this process
8924 
8925   Level: developer
8926 
8927 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8928 @*/
8929 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8930 {
8931   DM_Plex *mesh = (DM_Plex *)dm->data;
8932 
8933   PetscFunctionBegin;
8934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8935   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8936   *globalCellNumbers = mesh->globalCellNumbers;
8937   PetscFunctionReturn(PETSC_SUCCESS);
8938 }
8939 
8940 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8941 {
8942   PetscInt vStart, vEnd;
8943 
8944   PetscFunctionBegin;
8945   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8946   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8947   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8948   PetscFunctionReturn(PETSC_SUCCESS);
8949 }
8950 
8951 /*@
8952   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8953 
8954   Input Parameter:
8955 . dm - The `DMPLEX` object
8956 
8957   Output Parameter:
8958 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8959 
8960   Level: developer
8961 
8962 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8963 @*/
8964 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8965 {
8966   DM_Plex *mesh = (DM_Plex *)dm->data;
8967 
8968   PetscFunctionBegin;
8969   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8970   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8971   *globalVertexNumbers = mesh->globalVertexNumbers;
8972   PetscFunctionReturn(PETSC_SUCCESS);
8973 }
8974 
8975 /*@
8976   DMPlexCreatePointNumbering - Create a global numbering for all points.
8977 
8978   Collective
8979 
8980   Input Parameter:
8981 . dm - The `DMPLEX` object
8982 
8983   Output Parameter:
8984 . globalPointNumbers - Global numbers for all points on this process
8985 
8986   Level: developer
8987 
8988   Notes:
8989   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8990   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8991   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8992   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8993 
8994   The partitioned mesh is
8995   ```
8996   (2)--0--(3)--1--(4)    (1)--0--(2)
8997   ```
8998   and its global numbering is
8999   ```
9000   (3)--0--(4)--1--(5)--2--(6)
9001   ```
9002   Then the global numbering is provided as
9003   ```
9004   [0] Number of indices in set 5
9005   [0] 0 0
9006   [0] 1 1
9007   [0] 2 3
9008   [0] 3 4
9009   [0] 4 -6
9010   [1] Number of indices in set 3
9011   [1] 0 2
9012   [1] 1 5
9013   [1] 2 6
9014   ```
9015 
9016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9017 @*/
9018 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9019 {
9020   IS        nums[4];
9021   PetscInt  depths[4], gdepths[4], starts[4];
9022   PetscInt  depth, d, shift = 0;
9023   PetscBool empty = PETSC_FALSE;
9024 
9025   PetscFunctionBegin;
9026   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9027   PetscCall(DMPlexGetDepth(dm, &depth));
9028   // For unstratified meshes use dim instead of depth
9029   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9030   // If any stratum is empty, we must mark all empty
9031   for (d = 0; d <= depth; ++d) {
9032     PetscInt end;
9033 
9034     depths[d] = depth - d;
9035     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9036     if (!(starts[d] - end)) empty = PETSC_TRUE;
9037   }
9038   if (empty)
9039     for (d = 0; d <= depth; ++d) {
9040       depths[d] = -1;
9041       starts[d] = -1;
9042     }
9043   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9044   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9045   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]);
9046   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9047   for (d = 0; d <= depth; ++d) {
9048     PetscInt pStart, pEnd, gsize;
9049 
9050     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9051     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9052     shift += gsize;
9053   }
9054   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9055   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9056   PetscFunctionReturn(PETSC_SUCCESS);
9057 }
9058 
9059 /*@
9060   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9061 
9062   Collective
9063 
9064   Input Parameter:
9065 . dm - The `DMPLEX` object
9066 
9067   Output Parameter:
9068 . globalEdgeNumbers - Global numbers for all edges on this process
9069 
9070   Level: developer
9071 
9072   Notes:
9073   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).
9074 
9075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9076 @*/
9077 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9078 {
9079   PetscSF  sf;
9080   PetscInt eStart, eEnd;
9081 
9082   PetscFunctionBegin;
9083   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9084   PetscCall(DMGetPointSF(dm, &sf));
9085   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9086   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9087   PetscFunctionReturn(PETSC_SUCCESS);
9088 }
9089 
9090 /*@
9091   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9092 
9093   Input Parameter:
9094 . dm - The `DMPLEX` object
9095 
9096   Output Parameter:
9097 . ranks - The rank field
9098 
9099   Options Database Key:
9100 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9101 
9102   Level: intermediate
9103 
9104 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9105 @*/
9106 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9107 {
9108   DM             rdm;
9109   PetscFE        fe;
9110   PetscScalar   *r;
9111   PetscMPIInt    rank;
9112   DMPolytopeType ct;
9113   PetscInt       dim, cStart, cEnd, c;
9114   PetscBool      simplex;
9115 
9116   PetscFunctionBeginUser;
9117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9118   PetscAssertPointer(ranks, 2);
9119   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9120   PetscCall(DMClone(dm, &rdm));
9121   PetscCall(DMGetDimension(rdm, &dim));
9122   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9123   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9124   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9125   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9126   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9127   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9128   PetscCall(PetscFEDestroy(&fe));
9129   PetscCall(DMCreateDS(rdm));
9130   PetscCall(DMCreateGlobalVector(rdm, ranks));
9131   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9132   PetscCall(VecGetArray(*ranks, &r));
9133   for (c = cStart; c < cEnd; ++c) {
9134     PetscScalar *lr;
9135 
9136     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9137     if (lr) *lr = rank;
9138   }
9139   PetscCall(VecRestoreArray(*ranks, &r));
9140   PetscCall(DMDestroy(&rdm));
9141   PetscFunctionReturn(PETSC_SUCCESS);
9142 }
9143 
9144 /*@
9145   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9146 
9147   Input Parameters:
9148 + dm    - The `DMPLEX`
9149 - label - The `DMLabel`
9150 
9151   Output Parameter:
9152 . val - The label value field
9153 
9154   Options Database Key:
9155 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9156 
9157   Level: intermediate
9158 
9159 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9160 @*/
9161 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9162 {
9163   DM             rdm, plex;
9164   Vec            lval;
9165   PetscSection   section;
9166   PetscFE        fe;
9167   PetscScalar   *v;
9168   PetscInt       dim, pStart, pEnd, p, cStart;
9169   DMPolytopeType ct;
9170   char           name[PETSC_MAX_PATH_LEN];
9171   const char    *lname, *prefix;
9172 
9173   PetscFunctionBeginUser;
9174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9175   PetscAssertPointer(label, 2);
9176   PetscAssertPointer(val, 3);
9177   PetscCall(DMClone(dm, &rdm));
9178   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9179   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9180   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9181   PetscCall(DMDestroy(&plex));
9182   PetscCall(DMGetDimension(rdm, &dim));
9183   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9184   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9185   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9186   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9187   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9188   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9189   PetscCall(PetscFEDestroy(&fe));
9190   PetscCall(DMCreateDS(rdm));
9191   PetscCall(DMCreateGlobalVector(rdm, val));
9192   PetscCall(DMCreateLocalVector(rdm, &lval));
9193   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9194   PetscCall(DMGetLocalSection(rdm, &section));
9195   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9196   PetscCall(VecGetArray(lval, &v));
9197   for (p = pStart; p < pEnd; ++p) {
9198     PetscInt cval, dof, off;
9199 
9200     PetscCall(PetscSectionGetDof(section, p, &dof));
9201     if (!dof) continue;
9202     PetscCall(DMLabelGetValue(label, p, &cval));
9203     PetscCall(PetscSectionGetOffset(section, p, &off));
9204     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9205   }
9206   PetscCall(VecRestoreArray(lval, &v));
9207   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9208   PetscCall(VecDestroy(&lval));
9209   PetscCall(DMDestroy(&rdm));
9210   PetscFunctionReturn(PETSC_SUCCESS);
9211 }
9212 
9213 /*@
9214   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9215 
9216   Input Parameter:
9217 . dm - The `DMPLEX` object
9218 
9219   Level: developer
9220 
9221   Notes:
9222   This is a useful diagnostic when creating meshes programmatically.
9223 
9224   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9225 
9226 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9227 @*/
9228 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9229 {
9230   PetscSection    coneSection, supportSection;
9231   const PetscInt *cone, *support;
9232   PetscInt        coneSize, c, supportSize, s;
9233   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9234   PetscBool       storagecheck = PETSC_TRUE;
9235 
9236   PetscFunctionBegin;
9237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9238   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9239   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9240   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9241   /* Check that point p is found in the support of its cone points, and vice versa */
9242   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9243   for (p = pStart; p < pEnd; ++p) {
9244     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9245     PetscCall(DMPlexGetCone(dm, p, &cone));
9246     for (c = 0; c < coneSize; ++c) {
9247       PetscBool dup = PETSC_FALSE;
9248       PetscInt  d;
9249       for (d = c - 1; d >= 0; --d) {
9250         if (cone[c] == cone[d]) {
9251           dup = PETSC_TRUE;
9252           break;
9253         }
9254       }
9255       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9256       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9257       for (s = 0; s < supportSize; ++s) {
9258         if (support[s] == p) break;
9259       }
9260       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9261         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9262         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9263         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9264         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9265         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9266         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9267         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]);
9268         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9269       }
9270     }
9271     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9272     if (p != pp) {
9273       storagecheck = PETSC_FALSE;
9274       continue;
9275     }
9276     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9277     PetscCall(DMPlexGetSupport(dm, p, &support));
9278     for (s = 0; s < supportSize; ++s) {
9279       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9280       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9281       for (c = 0; c < coneSize; ++c) {
9282         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9283         if (cone[c] != pp) {
9284           c = 0;
9285           break;
9286         }
9287         if (cone[c] == p) break;
9288       }
9289       if (c >= coneSize) {
9290         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9291         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9292         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9293         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9294         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9295         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9296         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9297       }
9298     }
9299   }
9300   if (storagecheck) {
9301     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9302     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9303     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9304   }
9305   PetscFunctionReturn(PETSC_SUCCESS);
9306 }
9307 
9308 /*
9309   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.
9310 */
9311 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9312 {
9313   DMPolytopeType  cct;
9314   PetscInt        ptpoints[4];
9315   const PetscInt *cone, *ccone, *ptcone;
9316   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9317 
9318   PetscFunctionBegin;
9319   *unsplit = 0;
9320   switch (ct) {
9321   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9322     ptpoints[npt++] = c;
9323     break;
9324   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9325     PetscCall(DMPlexGetCone(dm, c, &cone));
9326     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9327     for (cp = 0; cp < coneSize; ++cp) {
9328       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9329       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9330     }
9331     break;
9332   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9333   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9334     PetscCall(DMPlexGetCone(dm, c, &cone));
9335     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9336     for (cp = 0; cp < coneSize; ++cp) {
9337       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9338       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9339       for (ccp = 0; ccp < cconeSize; ++ccp) {
9340         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9341         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9342           PetscInt p;
9343           for (p = 0; p < npt; ++p)
9344             if (ptpoints[p] == ccone[ccp]) break;
9345           if (p == npt) ptpoints[npt++] = ccone[ccp];
9346         }
9347       }
9348     }
9349     break;
9350   default:
9351     break;
9352   }
9353   for (pt = 0; pt < npt; ++pt) {
9354     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9355     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9356   }
9357   PetscFunctionReturn(PETSC_SUCCESS);
9358 }
9359 
9360 /*@
9361   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9362 
9363   Input Parameters:
9364 + dm         - The `DMPLEX` object
9365 - cellHeight - Normally 0
9366 
9367   Level: developer
9368 
9369   Notes:
9370   This is a useful diagnostic when creating meshes programmatically.
9371   Currently applicable only to homogeneous simplex or tensor meshes.
9372 
9373   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9374 
9375 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9376 @*/
9377 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9378 {
9379   DMPlexInterpolatedFlag interp;
9380   DMPolytopeType         ct;
9381   PetscInt               vStart, vEnd, cStart, cEnd, c;
9382 
9383   PetscFunctionBegin;
9384   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9385   PetscCall(DMPlexIsInterpolated(dm, &interp));
9386   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9387   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9388   for (c = cStart; c < cEnd; ++c) {
9389     PetscInt *closure = NULL;
9390     PetscInt  coneSize, closureSize, cl, Nv = 0;
9391 
9392     PetscCall(DMPlexGetCellType(dm, c, &ct));
9393     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9394     if (interp == DMPLEX_INTERPOLATED_FULL) {
9395       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9396       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));
9397     }
9398     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9399     for (cl = 0; cl < closureSize * 2; cl += 2) {
9400       const PetscInt p = closure[cl];
9401       if ((p >= vStart) && (p < vEnd)) ++Nv;
9402     }
9403     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9404     /* Special Case: Tensor faces with identified vertices */
9405     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9406       PetscInt unsplit;
9407 
9408       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9409       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9410     }
9411     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));
9412   }
9413   PetscFunctionReturn(PETSC_SUCCESS);
9414 }
9415 
9416 /*@
9417   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9418 
9419   Collective
9420 
9421   Input Parameters:
9422 + dm         - The `DMPLEX` object
9423 - cellHeight - Normally 0
9424 
9425   Level: developer
9426 
9427   Notes:
9428   This is a useful diagnostic when creating meshes programmatically.
9429   This routine is only relevant for meshes that are fully interpolated across all ranks.
9430   It will error out if a partially interpolated mesh is given on some rank.
9431   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9432 
9433   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9434 
9435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9436 @*/
9437 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9438 {
9439   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9440   DMPlexInterpolatedFlag interpEnum;
9441 
9442   PetscFunctionBegin;
9443   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9444   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9445   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9446   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9447     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9448     PetscFunctionReturn(PETSC_SUCCESS);
9449   }
9450 
9451   PetscCall(DMGetDimension(dm, &dim));
9452   PetscCall(DMPlexGetDepth(dm, &depth));
9453   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9454   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9455     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9456     for (c = cStart; c < cEnd; ++c) {
9457       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9458       const DMPolytopeType *faceTypes;
9459       DMPolytopeType        ct;
9460       PetscInt              numFaces, coneSize, f;
9461       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9462 
9463       PetscCall(DMPlexGetCellType(dm, c, &ct));
9464       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9465       if (unsplit) continue;
9466       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9467       PetscCall(DMPlexGetCone(dm, c, &cone));
9468       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9469       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9470       for (cl = 0; cl < closureSize * 2; cl += 2) {
9471         const PetscInt p = closure[cl];
9472         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9473       }
9474       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9475       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);
9476       for (f = 0; f < numFaces; ++f) {
9477         DMPolytopeType fct;
9478         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9479 
9480         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9481         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9482         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9483           const PetscInt p = fclosure[cl];
9484           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9485         }
9486         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]);
9487         for (v = 0; v < fnumCorners; ++v) {
9488           if (fclosure[v] != faces[fOff + v]) {
9489             PetscInt v1;
9490 
9491             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9492             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9493             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9494             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9495             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9496             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]);
9497           }
9498         }
9499         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9500         fOff += faceSizes[f];
9501       }
9502       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9503       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9504     }
9505   }
9506   PetscFunctionReturn(PETSC_SUCCESS);
9507 }
9508 
9509 /*@
9510   DMPlexCheckGeometry - Check the geometry of mesh cells
9511 
9512   Input Parameter:
9513 . dm - The `DMPLEX` object
9514 
9515   Level: developer
9516 
9517   Notes:
9518   This is a useful diagnostic when creating meshes programmatically.
9519 
9520   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9521 
9522 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9523 @*/
9524 PetscErrorCode DMPlexCheckGeometry(DM dm)
9525 {
9526   Vec       coordinates;
9527   PetscReal detJ, J[9], refVol = 1.0;
9528   PetscReal vol;
9529   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9530 
9531   PetscFunctionBegin;
9532   PetscCall(DMGetDimension(dm, &dim));
9533   PetscCall(DMGetCoordinateDim(dm, &dE));
9534   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9535   PetscCall(DMPlexGetDepth(dm, &depth));
9536   for (d = 0; d < dim; ++d) refVol *= 2.0;
9537   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9538   /* Make sure local coordinates are created, because that step is collective */
9539   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9540   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9541   for (c = cStart; c < cEnd; ++c) {
9542     DMPolytopeType ct;
9543     PetscInt       unsplit;
9544     PetscBool      ignoreZeroVol = PETSC_FALSE;
9545 
9546     PetscCall(DMPlexGetCellType(dm, c, &ct));
9547     switch (ct) {
9548     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9549     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9550     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9551       ignoreZeroVol = PETSC_TRUE;
9552       break;
9553     default:
9554       break;
9555     }
9556     switch (ct) {
9557     case DM_POLYTOPE_TRI_PRISM:
9558     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9559     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9560     case DM_POLYTOPE_PYRAMID:
9561       continue;
9562     default:
9563       break;
9564     }
9565     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9566     if (unsplit) continue;
9567     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9568     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);
9569     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9570     /* This should work with periodicity since DG coordinates should be used */
9571     if (depth > 1) {
9572       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9573       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);
9574       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9575     }
9576   }
9577   PetscFunctionReturn(PETSC_SUCCESS);
9578 }
9579 
9580 /*@
9581   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9582 
9583   Collective
9584 
9585   Input Parameters:
9586 + dm              - The `DMPLEX` object
9587 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9588 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9589 
9590   Level: developer
9591 
9592   Notes:
9593   This is mainly intended for debugging/testing purposes.
9594 
9595   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9596 
9597   Extra roots can come from periodic cuts, where additional points appear on the boundary
9598 
9599 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9600 @*/
9601 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9602 {
9603   PetscInt           l, nleaves, nroots, overlap;
9604   const PetscInt    *locals;
9605   const PetscSFNode *remotes;
9606   PetscBool          distributed;
9607   MPI_Comm           comm;
9608   PetscMPIInt        rank;
9609 
9610   PetscFunctionBegin;
9611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9612   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9613   else pointSF = dm->sf;
9614   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9615   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9616   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9617   {
9618     PetscMPIInt mpiFlag;
9619 
9620     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9621     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9622   }
9623   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9624   PetscCall(DMPlexIsDistributed(dm, &distributed));
9625   if (!distributed) {
9626     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);
9627     PetscFunctionReturn(PETSC_SUCCESS);
9628   }
9629   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);
9630   PetscCall(DMPlexGetOverlap(dm, &overlap));
9631 
9632   /* Check SF graph is compatible with DMPlex chart */
9633   {
9634     PetscInt pStart, pEnd, maxLeaf;
9635 
9636     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9637     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9638     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9639     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9640   }
9641 
9642   /* Check there are no cells in interface */
9643   if (!overlap) {
9644     PetscInt cellHeight, cStart, cEnd;
9645 
9646     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9647     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9648     for (l = 0; l < nleaves; ++l) {
9649       const PetscInt point = locals ? locals[l] : l;
9650 
9651       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9652     }
9653   }
9654 
9655   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9656   {
9657     const PetscInt *rootdegree;
9658 
9659     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9660     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9661     for (l = 0; l < nleaves; ++l) {
9662       const PetscInt  point = locals ? locals[l] : l;
9663       const PetscInt *cone;
9664       PetscInt        coneSize, c, idx;
9665 
9666       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9667       PetscCall(DMPlexGetCone(dm, point, &cone));
9668       for (c = 0; c < coneSize; ++c) {
9669         if (!rootdegree[cone[c]]) {
9670           if (locals) {
9671             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9672           } else {
9673             idx = (cone[c] < nleaves) ? cone[c] : -1;
9674           }
9675           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9676         }
9677       }
9678     }
9679   }
9680   PetscFunctionReturn(PETSC_SUCCESS);
9681 }
9682 
9683 /*@
9684   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9685 
9686   Collective
9687 
9688   Input Parameter:
9689 . dm - The `DMPLEX` object
9690 
9691   Level: developer
9692 
9693   Notes:
9694   This is mainly intended for debugging/testing purposes.
9695 
9696   Other cell types which are disconnected would be caught by the symmetry and face checks.
9697 
9698   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9699 
9700 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9701 @*/
9702 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9703 {
9704   PetscInt pStart, pEnd, vStart, vEnd;
9705 
9706   PetscFunctionBegin;
9707   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9708   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9709   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9710   for (PetscInt v = vStart; v < vEnd; ++v) {
9711     PetscInt suppSize;
9712 
9713     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9714     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9715   }
9716   PetscFunctionReturn(PETSC_SUCCESS);
9717 }
9718 
9719 /*@
9720   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9721 
9722   Input Parameter:
9723 . dm - The `DMPLEX` object
9724 
9725   Level: developer
9726 
9727   Notes:
9728   This is a useful diagnostic when creating meshes programmatically.
9729 
9730   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9731 
9732   Currently does not include `DMPlexCheckCellShape()`.
9733 
9734 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9735 @*/
9736 PetscErrorCode DMPlexCheck(DM dm)
9737 {
9738   PetscInt cellHeight;
9739 
9740   PetscFunctionBegin;
9741   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9742   PetscCall(DMPlexCheckSymmetry(dm));
9743   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9744   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9745   PetscCall(DMPlexCheckGeometry(dm));
9746   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9747   PetscCall(DMPlexCheckInterfaceCones(dm));
9748   PetscCall(DMPlexCheckOrphanVertices(dm));
9749   PetscFunctionReturn(PETSC_SUCCESS);
9750 }
9751 
9752 typedef struct cell_stats {
9753   PetscReal min, max, sum, squaresum;
9754   PetscInt  count;
9755 } cell_stats_t;
9756 
9757 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9758 {
9759   PetscInt i, N = *len;
9760 
9761   for (i = 0; i < N; i++) {
9762     cell_stats_t *A = (cell_stats_t *)a;
9763     cell_stats_t *B = (cell_stats_t *)b;
9764 
9765     B->min = PetscMin(A->min, B->min);
9766     B->max = PetscMax(A->max, B->max);
9767     B->sum += A->sum;
9768     B->squaresum += A->squaresum;
9769     B->count += A->count;
9770   }
9771 }
9772 
9773 /*@
9774   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9775 
9776   Collective
9777 
9778   Input Parameters:
9779 + dm        - The `DMPLEX` object
9780 . output    - If true, statistics will be displayed on `stdout`
9781 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9782 
9783   Level: developer
9784 
9785   Notes:
9786   This is mainly intended for debugging/testing purposes.
9787 
9788   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9789 
9790 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9791 @*/
9792 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9793 {
9794   DM           dmCoarse;
9795   cell_stats_t stats, globalStats;
9796   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9797   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9798   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9799   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9800   PetscMPIInt  rank, size;
9801 
9802   PetscFunctionBegin;
9803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9804   stats.min = PETSC_MAX_REAL;
9805   stats.max = PETSC_MIN_REAL;
9806   stats.sum = stats.squaresum = 0.;
9807   stats.count                 = 0;
9808 
9809   PetscCallMPI(MPI_Comm_size(comm, &size));
9810   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9811   PetscCall(DMGetCoordinateDim(dm, &cdim));
9812   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9813   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9814   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9815   for (c = cStart; c < cEnd; c++) {
9816     PetscInt  i;
9817     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9818 
9819     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9820     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9821     for (i = 0; i < PetscSqr(cdim); ++i) {
9822       frobJ += J[i] * J[i];
9823       frobInvJ += invJ[i] * invJ[i];
9824     }
9825     cond2 = frobJ * frobInvJ;
9826     cond  = PetscSqrtReal(cond2);
9827 
9828     stats.min = PetscMin(stats.min, cond);
9829     stats.max = PetscMax(stats.max, cond);
9830     stats.sum += cond;
9831     stats.squaresum += cond2;
9832     stats.count++;
9833     if (output && cond > limit) {
9834       PetscSection coordSection;
9835       Vec          coordsLocal;
9836       PetscScalar *coords = NULL;
9837       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9838 
9839       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9840       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9841       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9842       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9843       for (i = 0; i < Nv / cdim; ++i) {
9844         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9845         for (d = 0; d < cdim; ++d) {
9846           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9847           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9848         }
9849         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9850       }
9851       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9852       for (cl = 0; cl < clSize * 2; cl += 2) {
9853         const PetscInt edge = closure[cl];
9854 
9855         if ((edge >= eStart) && (edge < eEnd)) {
9856           PetscReal len;
9857 
9858           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9859           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9860         }
9861       }
9862       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9863       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9864     }
9865   }
9866   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9867 
9868   if (size > 1) {
9869     PetscMPIInt  blockLengths[2] = {4, 1};
9870     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9871     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9872     MPI_Op       statReduce;
9873 
9874     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9875     PetscCallMPI(MPI_Type_commit(&statType));
9876     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9877     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9878     PetscCallMPI(MPI_Op_free(&statReduce));
9879     PetscCallMPI(MPI_Type_free(&statType));
9880   } else {
9881     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9882   }
9883   if (rank == 0) {
9884     count = globalStats.count;
9885     min   = globalStats.min;
9886     max   = globalStats.max;
9887     mean  = globalStats.sum / globalStats.count;
9888     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9889   }
9890 
9891   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));
9892   PetscCall(PetscFree2(J, invJ));
9893 
9894   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9895   if (dmCoarse) {
9896     PetscBool isplex;
9897 
9898     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9899     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9900   }
9901   PetscFunctionReturn(PETSC_SUCCESS);
9902 }
9903 
9904 /*@
9905   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9906   orthogonal quality below given tolerance.
9907 
9908   Collective
9909 
9910   Input Parameters:
9911 + dm   - The `DMPLEX` object
9912 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9913 - atol - [0, 1] Absolute tolerance for tagging cells.
9914 
9915   Output Parameters:
9916 + OrthQual      - `Vec` containing orthogonal quality per cell
9917 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9918 
9919   Options Database Keys:
9920 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9921 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9922 
9923   Level: intermediate
9924 
9925   Notes:
9926   Orthogonal quality is given by the following formula\:
9927 
9928   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9929 
9930   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
9931   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9932   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9933   calculating the cosine of the angle between these vectors.
9934 
9935   Orthogonal quality ranges from 1 (best) to 0 (worst).
9936 
9937   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9938   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9939 
9940   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9941 
9942 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9943 @*/
9944 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9945 {
9946   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9947   PetscInt              *idx;
9948   PetscScalar           *oqVals;
9949   const PetscScalar     *cellGeomArr, *faceGeomArr;
9950   PetscReal             *ci, *fi, *Ai;
9951   MPI_Comm               comm;
9952   Vec                    cellgeom, facegeom;
9953   DM                     dmFace, dmCell;
9954   IS                     glob;
9955   ISLocalToGlobalMapping ltog;
9956   PetscViewer            vwr;
9957 
9958   PetscFunctionBegin;
9959   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9960   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9961   PetscAssertPointer(OrthQual, 4);
9962   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9963   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9964   PetscCall(DMGetDimension(dm, &nc));
9965   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9966   {
9967     DMPlexInterpolatedFlag interpFlag;
9968 
9969     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9970     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9971       PetscMPIInt rank;
9972 
9973       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9974       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9975     }
9976   }
9977   if (OrthQualLabel) {
9978     PetscAssertPointer(OrthQualLabel, 5);
9979     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9980     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9981   } else {
9982     *OrthQualLabel = NULL;
9983   }
9984   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9985   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9986   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9987   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9988   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9989   PetscCall(VecCreate(comm, OrthQual));
9990   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9991   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9992   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9993   PetscCall(VecSetUp(*OrthQual));
9994   PetscCall(ISDestroy(&glob));
9995   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9996   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9997   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9998   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9999   PetscCall(VecGetDM(cellgeom, &dmCell));
10000   PetscCall(VecGetDM(facegeom, &dmFace));
10001   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10002   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10003     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10004     PetscInt         cellarr[2], *adj = NULL;
10005     PetscScalar     *cArr, *fArr;
10006     PetscReal        minvalc = 1.0, minvalf = 1.0;
10007     PetscFVCellGeom *cg;
10008 
10009     idx[cellIter] = cell - cStart;
10010     cellarr[0]    = cell;
10011     /* Make indexing into cellGeom easier */
10012     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10013     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10014     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10015     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10016     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10017       PetscInt         i;
10018       const PetscInt   neigh  = adj[cellneigh];
10019       PetscReal        normci = 0, normfi = 0, normai = 0;
10020       PetscFVCellGeom *cgneigh;
10021       PetscFVFaceGeom *fg;
10022 
10023       /* Don't count ourselves in the neighbor list */
10024       if (neigh == cell) continue;
10025       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10026       cellarr[1] = neigh;
10027       {
10028         PetscInt        numcovpts;
10029         const PetscInt *covpts;
10030 
10031         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10032         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10033         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10034       }
10035 
10036       /* Compute c_i, f_i and their norms */
10037       for (i = 0; i < nc; i++) {
10038         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10039         fi[i] = fg->centroid[i] - cg->centroid[i];
10040         Ai[i] = fg->normal[i];
10041         normci += PetscPowReal(ci[i], 2);
10042         normfi += PetscPowReal(fi[i], 2);
10043         normai += PetscPowReal(Ai[i], 2);
10044       }
10045       normci = PetscSqrtReal(normci);
10046       normfi = PetscSqrtReal(normfi);
10047       normai = PetscSqrtReal(normai);
10048 
10049       /* Normalize and compute for each face-cell-normal pair */
10050       for (i = 0; i < nc; i++) {
10051         ci[i] = ci[i] / normci;
10052         fi[i] = fi[i] / normfi;
10053         Ai[i] = Ai[i] / normai;
10054         /* PetscAbs because I don't know if normals are guaranteed to point out */
10055         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10056         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10057       }
10058       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10059       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10060     }
10061     PetscCall(PetscFree(adj));
10062     PetscCall(PetscFree2(cArr, fArr));
10063     /* Defer to cell if they're equal */
10064     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10065     if (OrthQualLabel) {
10066       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10067     }
10068   }
10069   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10070   PetscCall(VecAssemblyBegin(*OrthQual));
10071   PetscCall(VecAssemblyEnd(*OrthQual));
10072   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10073   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10074   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10075   if (OrthQualLabel) {
10076     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10077   }
10078   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10079   PetscCall(PetscViewerDestroy(&vwr));
10080   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10081   PetscFunctionReturn(PETSC_SUCCESS);
10082 }
10083 
10084 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10085  * interpolator construction */
10086 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10087 {
10088   PetscSection section, newSection, gsection;
10089   PetscSF      sf;
10090   PetscBool    hasConstraints, ghasConstraints;
10091 
10092   PetscFunctionBegin;
10093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10094   PetscAssertPointer(odm, 2);
10095   PetscCall(DMGetLocalSection(dm, &section));
10096   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10097   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10098   if (!ghasConstraints) {
10099     PetscCall(PetscObjectReference((PetscObject)dm));
10100     *odm = dm;
10101     PetscFunctionReturn(PETSC_SUCCESS);
10102   }
10103   PetscCall(DMClone(dm, odm));
10104   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10105   PetscCall(DMGetLocalSection(*odm, &newSection));
10106   PetscCall(DMGetPointSF(*odm, &sf));
10107   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10108   PetscCall(DMSetGlobalSection(*odm, gsection));
10109   PetscCall(PetscSectionDestroy(&gsection));
10110   PetscFunctionReturn(PETSC_SUCCESS);
10111 }
10112 
10113 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10114 {
10115   DM        dmco, dmfo;
10116   Mat       interpo;
10117   Vec       rscale;
10118   Vec       cglobalo, clocal;
10119   Vec       fglobal, fglobalo, flocal;
10120   PetscBool regular;
10121 
10122   PetscFunctionBegin;
10123   PetscCall(DMGetFullDM(dmc, &dmco));
10124   PetscCall(DMGetFullDM(dmf, &dmfo));
10125   PetscCall(DMSetCoarseDM(dmfo, dmco));
10126   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10127   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10128   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10129   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10130   PetscCall(DMCreateLocalVector(dmc, &clocal));
10131   PetscCall(VecSet(cglobalo, 0.));
10132   PetscCall(VecSet(clocal, 0.));
10133   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10134   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10135   PetscCall(DMCreateLocalVector(dmf, &flocal));
10136   PetscCall(VecSet(fglobal, 0.));
10137   PetscCall(VecSet(fglobalo, 0.));
10138   PetscCall(VecSet(flocal, 0.));
10139   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10140   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10141   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10142   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10143   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10144   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10145   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10146   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10147   *shift = fglobal;
10148   PetscCall(VecDestroy(&flocal));
10149   PetscCall(VecDestroy(&fglobalo));
10150   PetscCall(VecDestroy(&clocal));
10151   PetscCall(VecDestroy(&cglobalo));
10152   PetscCall(VecDestroy(&rscale));
10153   PetscCall(MatDestroy(&interpo));
10154   PetscCall(DMDestroy(&dmfo));
10155   PetscCall(DMDestroy(&dmco));
10156   PetscFunctionReturn(PETSC_SUCCESS);
10157 }
10158 
10159 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10160 {
10161   PetscObject shifto;
10162   Vec         shift;
10163 
10164   PetscFunctionBegin;
10165   if (!interp) {
10166     Vec rscale;
10167 
10168     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10169     PetscCall(VecDestroy(&rscale));
10170   } else {
10171     PetscCall(PetscObjectReference((PetscObject)interp));
10172   }
10173   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10174   if (!shifto) {
10175     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10176     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10177     shifto = (PetscObject)shift;
10178     PetscCall(VecDestroy(&shift));
10179   }
10180   shift = (Vec)shifto;
10181   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10182   PetscCall(VecAXPY(fineSol, 1.0, shift));
10183   PetscCall(MatDestroy(&interp));
10184   PetscFunctionReturn(PETSC_SUCCESS);
10185 }
10186 
10187 /* Pointwise interpolation
10188      Just code FEM for now
10189      u^f = I u^c
10190      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10191      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10192      I_{ij} = psi^f_i phi^c_j
10193 */
10194 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10195 {
10196   PetscSection gsc, gsf;
10197   PetscInt     m, n;
10198   void        *ctx;
10199   DM           cdm;
10200   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10201 
10202   PetscFunctionBegin;
10203   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10204   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10205   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10206   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10207 
10208   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10209   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10210   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10211   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10212   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10213 
10214   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10215   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10216   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10217   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10218   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10219   if (scaling) {
10220     /* Use naive scaling */
10221     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10222   }
10223   PetscFunctionReturn(PETSC_SUCCESS);
10224 }
10225 
10226 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10227 {
10228   VecScatter ctx;
10229 
10230   PetscFunctionBegin;
10231   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10232   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10233   PetscCall(VecScatterDestroy(&ctx));
10234   PetscFunctionReturn(PETSC_SUCCESS);
10235 }
10236 
10237 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[])
10238 {
10239   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10240   const PetscInt Nc = uOff[f + 1] - uOff[f];
10241   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10242 }
10243 
10244 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10245 {
10246   DM           dmc;
10247   PetscDS      ds;
10248   Vec          ones, locmass;
10249   IS           cellIS;
10250   PetscFormKey key;
10251   PetscInt     depth;
10252 
10253   PetscFunctionBegin;
10254   PetscCall(DMClone(dm, &dmc));
10255   PetscCall(DMCopyDisc(dm, dmc));
10256   PetscCall(DMGetDS(dmc, &ds));
10257   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10258   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10259   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10260   else PetscCall(DMGetLocalVector(dm, &locmass));
10261   PetscCall(DMGetLocalVector(dm, &ones));
10262   PetscCall(DMPlexGetDepth(dm, &depth));
10263   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10264   PetscCall(VecSet(locmass, 0.0));
10265   PetscCall(VecSet(ones, 1.0));
10266   key.label = NULL;
10267   key.value = 0;
10268   key.field = 0;
10269   key.part  = 0;
10270   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10271   PetscCall(ISDestroy(&cellIS));
10272   if (mass) {
10273     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10274     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10275   }
10276   PetscCall(DMRestoreLocalVector(dm, &ones));
10277   if (lmass) *lmass = locmass;
10278   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10279   PetscCall(DMDestroy(&dmc));
10280   PetscFunctionReturn(PETSC_SUCCESS);
10281 }
10282 
10283 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10284 {
10285   PetscSection gsc, gsf;
10286   PetscInt     m, n;
10287   void        *ctx;
10288   DM           cdm;
10289   PetscBool    regular;
10290 
10291   PetscFunctionBegin;
10292   if (dmFine == dmCoarse) {
10293     DM            dmc;
10294     PetscDS       ds;
10295     PetscWeakForm wf;
10296     Vec           u;
10297     IS            cellIS;
10298     PetscFormKey  key;
10299     PetscInt      depth;
10300 
10301     PetscCall(DMClone(dmFine, &dmc));
10302     PetscCall(DMCopyDisc(dmFine, dmc));
10303     PetscCall(DMGetDS(dmc, &ds));
10304     PetscCall(PetscDSGetWeakForm(ds, &wf));
10305     PetscCall(PetscWeakFormClear(wf));
10306     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10307     PetscCall(DMCreateMatrix(dmc, mass));
10308     PetscCall(DMGetLocalVector(dmc, &u));
10309     PetscCall(DMPlexGetDepth(dmc, &depth));
10310     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10311     PetscCall(MatZeroEntries(*mass));
10312     key.label = NULL;
10313     key.value = 0;
10314     key.field = 0;
10315     key.part  = 0;
10316     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10317     PetscCall(ISDestroy(&cellIS));
10318     PetscCall(DMRestoreLocalVector(dmc, &u));
10319     PetscCall(DMDestroy(&dmc));
10320   } else {
10321     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10322     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10323     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10324     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10325 
10326     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10327     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10328     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10329     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10330 
10331     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10332     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10333     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10334     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10335   }
10336   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10337   PetscFunctionReturn(PETSC_SUCCESS);
10338 }
10339 
10340 /*@
10341   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10342 
10343   Input Parameter:
10344 . dm - The `DMPLEX` object
10345 
10346   Output Parameter:
10347 . regular - The flag
10348 
10349   Level: intermediate
10350 
10351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10352 @*/
10353 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10354 {
10355   PetscFunctionBegin;
10356   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10357   PetscAssertPointer(regular, 2);
10358   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10359   PetscFunctionReturn(PETSC_SUCCESS);
10360 }
10361 
10362 /*@
10363   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10364 
10365   Input Parameters:
10366 + dm      - The `DMPLEX` object
10367 - regular - The flag
10368 
10369   Level: intermediate
10370 
10371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10372 @*/
10373 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10374 {
10375   PetscFunctionBegin;
10376   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10377   ((DM_Plex *)dm->data)->regularRefinement = regular;
10378   PetscFunctionReturn(PETSC_SUCCESS);
10379 }
10380 
10381 /*@
10382   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10383   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10384 
10385   Not Collective
10386 
10387   Input Parameter:
10388 . dm - The `DMPLEX` object
10389 
10390   Output Parameters:
10391 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10392 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10393 
10394   Level: intermediate
10395 
10396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10397 @*/
10398 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10399 {
10400   DM_Plex *plex = (DM_Plex *)dm->data;
10401 
10402   PetscFunctionBegin;
10403   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10404   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10405   if (anchorSection) *anchorSection = plex->anchorSection;
10406   if (anchorIS) *anchorIS = plex->anchorIS;
10407   PetscFunctionReturn(PETSC_SUCCESS);
10408 }
10409 
10410 /*@
10411   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10412 
10413   Collective
10414 
10415   Input Parameters:
10416 + dm            - The `DMPLEX` object
10417 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10418                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10419 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10420 
10421   Level: intermediate
10422 
10423   Notes:
10424   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10425   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10426   combination of other points' degrees of freedom.
10427 
10428   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10429   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10430 
10431   The reference counts of `anchorSection` and `anchorIS` are incremented.
10432 
10433 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10434 @*/
10435 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10436 {
10437   DM_Plex    *plex = (DM_Plex *)dm->data;
10438   PetscMPIInt result;
10439 
10440   PetscFunctionBegin;
10441   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10442   if (anchorSection) {
10443     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10444     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10445     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10446   }
10447   if (anchorIS) {
10448     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10449     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10450     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10451   }
10452 
10453   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10454   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10455   plex->anchorSection = anchorSection;
10456 
10457   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10458   PetscCall(ISDestroy(&plex->anchorIS));
10459   plex->anchorIS = anchorIS;
10460 
10461   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10462     PetscInt        size, a, pStart, pEnd;
10463     const PetscInt *anchors;
10464 
10465     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10466     PetscCall(ISGetLocalSize(anchorIS, &size));
10467     PetscCall(ISGetIndices(anchorIS, &anchors));
10468     for (a = 0; a < size; a++) {
10469       PetscInt p;
10470 
10471       p = anchors[a];
10472       if (p >= pStart && p < pEnd) {
10473         PetscInt dof;
10474 
10475         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10476         if (dof) {
10477           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10478           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10479         }
10480       }
10481     }
10482     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10483   }
10484   /* reset the generic constraints */
10485   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10486   PetscFunctionReturn(PETSC_SUCCESS);
10487 }
10488 
10489 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10490 {
10491   PetscSection anchorSection;
10492   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10493 
10494   PetscFunctionBegin;
10495   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10496   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10497   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10498   PetscCall(PetscSectionGetNumFields(section, &numFields));
10499   if (numFields) {
10500     PetscInt f;
10501     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10502 
10503     for (f = 0; f < numFields; f++) {
10504       PetscInt numComp;
10505 
10506       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10507       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10508     }
10509   }
10510   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10511   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10512   pStart = PetscMax(pStart, sStart);
10513   pEnd   = PetscMin(pEnd, sEnd);
10514   pEnd   = PetscMax(pStart, pEnd);
10515   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10516   for (p = pStart; p < pEnd; p++) {
10517     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10518     if (dof) {
10519       PetscCall(PetscSectionGetDof(section, p, &dof));
10520       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10521       for (f = 0; f < numFields; f++) {
10522         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10523         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10524       }
10525     }
10526   }
10527   PetscCall(PetscSectionSetUp(*cSec));
10528   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10529   PetscFunctionReturn(PETSC_SUCCESS);
10530 }
10531 
10532 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10533 {
10534   PetscSection    aSec;
10535   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10536   const PetscInt *anchors;
10537   PetscInt        numFields, f;
10538   IS              aIS;
10539   MatType         mtype;
10540   PetscBool       iscuda, iskokkos;
10541 
10542   PetscFunctionBegin;
10543   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10544   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10545   PetscCall(PetscSectionGetStorageSize(section, &n));
10546   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10547   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10548   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10549   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10550   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10551   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10552   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10553   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10554   else mtype = MATSEQAIJ;
10555   PetscCall(MatSetType(*cMat, mtype));
10556   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10557   PetscCall(ISGetIndices(aIS, &anchors));
10558   /* cSec will be a subset of aSec and section */
10559   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10560   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10561   PetscCall(PetscMalloc1(m + 1, &i));
10562   i[0] = 0;
10563   PetscCall(PetscSectionGetNumFields(section, &numFields));
10564   for (p = pStart; p < pEnd; p++) {
10565     PetscInt rDof, rOff, r;
10566 
10567     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10568     if (!rDof) continue;
10569     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10570     if (numFields) {
10571       for (f = 0; f < numFields; f++) {
10572         annz = 0;
10573         for (r = 0; r < rDof; r++) {
10574           a = anchors[rOff + r];
10575           if (a < sStart || a >= sEnd) continue;
10576           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10577           annz += aDof;
10578         }
10579         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10580         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10581         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10582       }
10583     } else {
10584       annz = 0;
10585       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10586       for (q = 0; q < dof; q++) {
10587         a = anchors[rOff + q];
10588         if (a < sStart || a >= sEnd) continue;
10589         PetscCall(PetscSectionGetDof(section, a, &aDof));
10590         annz += aDof;
10591       }
10592       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10593       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10594       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10595     }
10596   }
10597   nnz = i[m];
10598   PetscCall(PetscMalloc1(nnz, &j));
10599   offset = 0;
10600   for (p = pStart; p < pEnd; p++) {
10601     if (numFields) {
10602       for (f = 0; f < numFields; f++) {
10603         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10604         for (q = 0; q < dof; q++) {
10605           PetscInt rDof, rOff, r;
10606           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10607           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10608           for (r = 0; r < rDof; r++) {
10609             PetscInt s;
10610 
10611             a = anchors[rOff + r];
10612             if (a < sStart || a >= sEnd) continue;
10613             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10614             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10615             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10616           }
10617         }
10618       }
10619     } else {
10620       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10621       for (q = 0; q < dof; q++) {
10622         PetscInt rDof, rOff, r;
10623         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10624         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10625         for (r = 0; r < rDof; r++) {
10626           PetscInt s;
10627 
10628           a = anchors[rOff + r];
10629           if (a < sStart || a >= sEnd) continue;
10630           PetscCall(PetscSectionGetDof(section, a, &aDof));
10631           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10632           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10633         }
10634       }
10635     }
10636   }
10637   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10638   PetscCall(PetscFree(i));
10639   PetscCall(PetscFree(j));
10640   PetscCall(ISRestoreIndices(aIS, &anchors));
10641   PetscFunctionReturn(PETSC_SUCCESS);
10642 }
10643 
10644 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10645 {
10646   DM_Plex     *plex = (DM_Plex *)dm->data;
10647   PetscSection anchorSection, section, cSec;
10648   Mat          cMat;
10649 
10650   PetscFunctionBegin;
10651   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10652   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10653   if (anchorSection) {
10654     PetscInt Nf;
10655 
10656     PetscCall(DMGetLocalSection(dm, &section));
10657     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10658     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10659     PetscCall(DMGetNumFields(dm, &Nf));
10660     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10661     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10662     PetscCall(PetscSectionDestroy(&cSec));
10663     PetscCall(MatDestroy(&cMat));
10664   }
10665   PetscFunctionReturn(PETSC_SUCCESS);
10666 }
10667 
10668 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10669 {
10670   IS           subis;
10671   PetscSection section, subsection;
10672 
10673   PetscFunctionBegin;
10674   PetscCall(DMGetLocalSection(dm, &section));
10675   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10676   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10677   /* Create subdomain */
10678   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10679   /* Create submodel */
10680   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10681   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10682   PetscCall(DMSetLocalSection(*subdm, subsection));
10683   PetscCall(PetscSectionDestroy(&subsection));
10684   PetscCall(DMCopyDisc(dm, *subdm));
10685   /* Create map from submodel to global model */
10686   if (is) {
10687     PetscSection    sectionGlobal, subsectionGlobal;
10688     IS              spIS;
10689     const PetscInt *spmap;
10690     PetscInt       *subIndices;
10691     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10692     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10693 
10694     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10695     PetscCall(ISGetIndices(spIS, &spmap));
10696     PetscCall(PetscSectionGetNumFields(section, &Nf));
10697     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10698     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10699     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10700     for (p = pStart; p < pEnd; ++p) {
10701       PetscInt gdof, pSubSize = 0;
10702 
10703       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10704       if (gdof > 0) {
10705         for (f = 0; f < Nf; ++f) {
10706           PetscInt fdof, fcdof;
10707 
10708           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10709           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10710           pSubSize += fdof - fcdof;
10711         }
10712         subSize += pSubSize;
10713         if (pSubSize) {
10714           if (bs < 0) {
10715             bs = pSubSize;
10716           } else if (bs != pSubSize) {
10717             /* Layout does not admit a pointwise block size */
10718             bs = 1;
10719           }
10720         }
10721       }
10722     }
10723     /* Must have same blocksize on all procs (some might have no points) */
10724     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10725     bsLocal[1] = bs;
10726     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10727     if (bsMinMax[0] != bsMinMax[1]) {
10728       bs = 1;
10729     } else {
10730       bs = bsMinMax[0];
10731     }
10732     PetscCall(PetscMalloc1(subSize, &subIndices));
10733     for (p = pStart; p < pEnd; ++p) {
10734       PetscInt gdof, goff;
10735 
10736       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10737       if (gdof > 0) {
10738         const PetscInt point = spmap[p];
10739 
10740         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10741         for (f = 0; f < Nf; ++f) {
10742           PetscInt fdof, fcdof, fc, f2, poff = 0;
10743 
10744           /* Can get rid of this loop by storing field information in the global section */
10745           for (f2 = 0; f2 < f; ++f2) {
10746             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10747             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10748             poff += fdof - fcdof;
10749           }
10750           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10751           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10752           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10753         }
10754       }
10755     }
10756     PetscCall(ISRestoreIndices(spIS, &spmap));
10757     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10758     if (bs > 1) {
10759       /* We need to check that the block size does not come from non-contiguous fields */
10760       PetscInt i, j, set = 1;
10761       for (i = 0; i < subSize; i += bs) {
10762         for (j = 0; j < bs; ++j) {
10763           if (subIndices[i + j] != subIndices[i] + j) {
10764             set = 0;
10765             break;
10766           }
10767         }
10768       }
10769       if (set) PetscCall(ISSetBlockSize(*is, bs));
10770     }
10771     /* Attach nullspace */
10772     for (f = 0; f < Nf; ++f) {
10773       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10774       if ((*subdm)->nullspaceConstructors[f]) break;
10775     }
10776     if (f < Nf) {
10777       MatNullSpace nullSpace;
10778       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10779 
10780       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10781       PetscCall(MatNullSpaceDestroy(&nullSpace));
10782     }
10783   }
10784   PetscFunctionReturn(PETSC_SUCCESS);
10785 }
10786 
10787 /*@
10788   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10789 
10790   Input Parameters:
10791 + dm    - The `DM`
10792 - dummy - unused argument
10793 
10794   Options Database Key:
10795 . -dm_plex_monitor_throughput - Activate the monitor
10796 
10797   Level: developer
10798 
10799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10800 @*/
10801 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10802 {
10803   PetscLogHandler default_handler;
10804 
10805   PetscFunctionBegin;
10806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10807   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10808   if (default_handler) {
10809     PetscLogEvent      event;
10810     PetscEventPerfInfo eventInfo;
10811     PetscLogDouble     cellRate, flopRate;
10812     PetscInt           cStart, cEnd, Nf, N;
10813     const char        *name;
10814 
10815     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10816     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10817     PetscCall(DMGetNumFields(dm, &Nf));
10818     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10819     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10820     N        = (cEnd - cStart) * Nf * eventInfo.count;
10821     flopRate = eventInfo.flops / eventInfo.time;
10822     cellRate = N / eventInfo.time;
10823     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, cellRate, flopRate / 1.e6));
10824   } else {
10825     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.");
10826   }
10827   PetscFunctionReturn(PETSC_SUCCESS);
10828 }
10829