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