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