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