xref: /petsc/src/dm/impls/plex/plex.c (revision f0b74427b291237450348b8514d67555ad08bce6)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_CreateBoxSFC, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 /* Logging support */
18 PetscLogEvent DMPLEX_DistributionView, DMPLEX_DistributionLoad;
19 
20 PetscBool  Plexcite       = PETSC_FALSE;
21 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
22                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
23                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
24                             "journal   = {SIAM Journal on Scientific Computing},\n"
25                             "volume    = {38},\n"
26                             "number    = {5},\n"
27                             "pages     = {S143--S155},\n"
28                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
29                             "doi       = {10.1137/15M1026092},\n"
30                             "year      = {2016},\n"
31                             "petsc_uses={DMPlex},\n}\n";
32 
33 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
34 
35 /*@
36   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
37 
38   Input Parameter:
39 . dm - The `DMPLEX` object
40 
41   Output Parameter:
42 . simplex - Flag checking for a simplex
43 
44   Level: intermediate
45 
46   Note:
47   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
48   If the mesh has no cells, this returns `PETSC_FALSE`.
49 
50 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
51 @*/
52 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
53 {
54   DMPolytopeType ct;
55   PetscInt       cStart, cEnd;
56 
57   PetscFunctionBegin;
58   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
59   if (cEnd <= cStart) {
60     *simplex = PETSC_FALSE;
61     PetscFunctionReturn(PETSC_SUCCESS);
62   }
63   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
64   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@
69   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
70 
71   Input Parameters:
72 + dm     - The `DMPLEX` object
73 - height - The cell height in the Plex, 0 is the default
74 
75   Output Parameters:
76 + cStart - The first "normal" cell, pass `NULL` if not needed
77 - cEnd   - The upper bound on "normal" cells, pass `NULL` if not needed
78 
79   Level: developer
80 
81   Note:
82   This function requires that tensor cells are ordered last.
83 
84 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
85 @*/
86 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PeOp PetscInt *cStart, PeOp PetscInt *cEnd)
87 {
88   DMLabel         ctLabel;
89   IS              valueIS;
90   const PetscInt *ctypes;
91   PetscBool       found = PETSC_FALSE;
92   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
93 
94   PetscFunctionBegin;
95   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
96   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
97   PetscCall(ISGetLocalSize(valueIS, &Nct));
98   PetscCall(ISGetIndices(valueIS, &ctypes));
99   for (PetscInt t = 0; t < Nct; ++t) {
100     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
101     PetscInt             ctS, ctE, ht;
102 
103     if (ct == DM_POLYTOPE_UNKNOWN) {
104       // If any cells are not typed, just use all cells
105       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
106       break;
107     }
108     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
109     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
110     if (ctS >= ctE) continue;
111     // Check that a point has the right height
112     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
113     if (ht != height) continue;
114     cS    = PetscMin(cS, ctS);
115     cE    = PetscMax(cE, ctE);
116     found = PETSC_TRUE;
117   }
118   if (!Nct || !found) cS = cE = 0;
119   PetscCall(ISDestroy(&valueIS));
120   // Reset label for fast lookup
121   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
122   if (cStart) *cStart = cS;
123   if (cEnd) *cEnd = cE;
124   PetscFunctionReturn(PETSC_SUCCESS);
125 }
126 
127 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
128 {
129   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
130   PetscInt                *sStart, *sEnd;
131   PetscViewerVTKFieldType *ft;
132   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
133   DMLabel                  depthLabel, ctLabel;
134 
135   PetscFunctionBegin;
136   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
137   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
138   PetscCall(DMGetCoordinateDim(dm, &cdim));
139   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
140   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
141   if (field >= 0) {
142     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
143   } else {
144     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
145   }
146 
147   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
148   PetscCall(DMPlexGetDepth(dm, &depth));
149   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
150   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
151   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
152     const DMPolytopeType ict = (DMPolytopeType)c;
153     PetscInt             dep;
154 
155     if (ict == DM_POLYTOPE_FV_GHOST) continue;
156     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
157     if (pStart >= 0) {
158       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
159       if (dep != depth - cellHeight) continue;
160     }
161     if (field >= 0) {
162       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
163     } else {
164       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
165     }
166   }
167 
168   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
169   *types = 0;
170 
171   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
172     if (globalvcdof[c]) ++(*types);
173   }
174 
175   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
176   t = 0;
177   if (globalvcdof[DM_NUM_POLYTOPES]) {
178     sStart[t] = vStart;
179     sEnd[t]   = vEnd;
180     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
181     ++t;
182   }
183 
184   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
185     if (globalvcdof[c]) {
186       const DMPolytopeType ict = (DMPolytopeType)c;
187 
188       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
189       sStart[t] = cStart;
190       sEnd[t]   = cEnd;
191       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
192       ++t;
193     }
194   }
195 
196   if (!*types) {
197     if (field >= 0) {
198       const char *fieldname;
199 
200       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
201       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
202     } else {
203       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
204     }
205   }
206 
207   *ssStart = sStart;
208   *ssEnd   = sEnd;
209   *sft     = ft;
210   PetscFunctionReturn(PETSC_SUCCESS);
211 }
212 
213 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
214 {
215   PetscFunctionBegin;
216   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
221 {
222   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
223   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
224 
225   PetscFunctionBegin;
226   *ft = PETSC_VTK_INVALID;
227   PetscCall(DMGetCoordinateDim(dm, &cdim));
228   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
229   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
230   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
231   if (field >= 0) {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
234   } else {
235     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
236     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
237   }
238   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
239   if (globalvcdof[0]) {
240     *sStart = vStart;
241     *sEnd   = vEnd;
242     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
243     else *ft = PETSC_VTK_POINT_FIELD;
244   } else if (globalvcdof[1]) {
245     *sStart = cStart;
246     *sEnd   = cEnd;
247     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
248     else *ft = PETSC_VTK_CELL_FIELD;
249   } else {
250     if (field >= 0) {
251       const char *fieldname;
252 
253       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
254       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
255     } else {
256       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section\n"));
257     }
258   }
259   PetscFunctionReturn(PETSC_SUCCESS);
260 }
261 
262 /*@
263   DMPlexVecView1D - Plot many 1D solutions on the same line graph
264 
265   Collective
266 
267   Input Parameters:
268 + dm     - The `DMPLEX` object
269 . n      - The number of vectors
270 . u      - The array of local vectors
271 - viewer - The `PetscViewer`
272 
273   Level: advanced
274 
275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
276 @*/
277 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
278 {
279   DM                 cdm;
280   PetscDS            ds;
281   PetscDraw          draw = NULL;
282   PetscDrawLG        lg;
283   Vec                coordinates;
284   const PetscScalar *coords, **sol;
285   PetscReal         *vals;
286   PetscInt          *Nc;
287   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
288   char             **names;
289 
290   PetscFunctionBegin;
291   PetscCall(DMGetCoordinateDM(dm, &cdm));
292   PetscCall(DMGetDS(dm, &ds));
293   PetscCall(PetscDSGetNumFields(ds, &Nf));
294   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
295   PetscCall(PetscDSGetComponents(ds, &Nc));
296 
297   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
298   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
299   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
300 
301   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
302   for (PetscInt i = 0, l = 0; i < n; ++i) {
303     const char *vname;
304 
305     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
306     for (PetscInt f = 0; f < Nf; ++f) {
307       PetscObject disc;
308       const char *fname;
309       char        tmpname[PETSC_MAX_PATH_LEN];
310 
311       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
312       /* TODO Create names for components */
313       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
314         PetscCall(PetscObjectGetName(disc, &fname));
315         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
316         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
317         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
318         PetscCall(PetscStrallocpy(tmpname, &names[l]));
319       }
320     }
321   }
322   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
323   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
324   PetscCall(VecGetArrayRead(coordinates, &coords));
325   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
326   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
327   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
328   PetscSection s;
329   PetscInt     cdof, vdof;
330 
331   PetscCall(DMGetLocalSection(dm, &s));
332   PetscCall(PetscSectionGetDof(s, eStart, &cdof));
333   PetscCall(PetscSectionGetDof(s, vStart, &vdof));
334   if (cdof) {
335     if (vdof) {
336       // P_2
337       PetscInt vFirst = -1;
338 
339       for (PetscInt e = eStart; e < eEnd; ++e) {
340         PetscScalar    *xa, *xb, *svals;
341         const PetscInt *cone;
342 
343         PetscCall(DMPlexGetCone(dm, e, &cone));
344         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
345         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
346         if (e == eStart) vFirst = cone[0];
347         for (PetscInt i = 0; i < n; ++i) {
348           PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
349           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
350         }
351         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
352         if (e == eEnd - 1 && cone[1] != vFirst) {
353           for (PetscInt i = 0; i < n; ++i) {
354             PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
355             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
356           }
357           PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
358           for (PetscInt i = 0; i < n; ++i) {
359             PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
360             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
361           }
362           PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
363         }
364       }
365     } else {
366       // P_0
367       for (PetscInt e = eStart; e < eEnd; ++e) {
368         PetscScalar    *xa, *xb, *svals;
369         const PetscInt *cone;
370 
371         PetscCall(DMPlexGetCone(dm, e, &cone));
372         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
373         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
374         for (PetscInt i = 0; i < n; ++i) {
375           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
376           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
377         }
378         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
379       }
380     }
381   } else if (vdof) {
382     // P_1
383     for (PetscInt v = vStart; v < vEnd; ++v) {
384       PetscScalar *x, *svals;
385 
386       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
387       for (PetscInt i = 0; i < n; ++i) {
388         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
389         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
390       }
391       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
392     }
393   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Discretization not supported");
394   PetscCall(VecRestoreArrayRead(coordinates, &coords));
395   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
396   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
397   PetscCall(PetscFree3(sol, names, vals));
398 
399   PetscCall(PetscDrawLGDraw(lg));
400   PetscCall(PetscDrawLGDestroy(&lg));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
405 {
406   DM dm;
407 
408   PetscFunctionBegin;
409   PetscCall(VecGetDM(u, &dm));
410   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
411   PetscFunctionReturn(PETSC_SUCCESS);
412 }
413 
414 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
415 {
416   DM                 dm;
417   PetscSection       s;
418   PetscDraw          draw, popup;
419   DM                 cdm;
420   PetscSection       coordSection;
421   Vec                coordinates;
422   const PetscScalar *array;
423   PetscReal          lbound[3], ubound[3];
424   PetscReal          vbound[2], time;
425   PetscBool          flg;
426   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
427   const char        *name;
428   char               title[PETSC_MAX_PATH_LEN];
429 
430   PetscFunctionBegin;
431   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
432   PetscCall(VecGetDM(v, &dm));
433   PetscCall(DMGetCoordinateDim(dm, &dim));
434   PetscCall(DMGetLocalSection(dm, &s));
435   PetscCall(PetscSectionGetNumFields(s, &Nf));
436   PetscCall(DMGetCoarsenLevel(dm, &level));
437   PetscCall(DMGetCoordinateDM(dm, &cdm));
438   PetscCall(DMGetLocalSection(cdm, &coordSection));
439   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
440   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
441   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
442 
443   PetscCall(PetscObjectGetName((PetscObject)v, &name));
444   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
445 
446   PetscCall(VecGetLocalSize(coordinates, &N));
447   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
448   PetscCall(PetscDrawClear(draw));
449 
450   /* Could implement something like DMDASelectFields() */
451   for (f = 0; f < Nf; ++f) {
452     DM          fdm = dm;
453     Vec         fv  = v;
454     IS          fis;
455     char        prefix[PETSC_MAX_PATH_LEN];
456     const char *fname;
457 
458     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
459     PetscCall(PetscSectionGetFieldName(s, f, &fname));
460 
461     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
462     else prefix[0] = '\0';
463     if (Nf > 1) {
464       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
465       PetscCall(VecGetSubVector(v, fis, &fv));
466       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
467       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
468     }
469     for (comp = 0; comp < Nc; ++comp, ++w) {
470       PetscInt nmax = 2;
471 
472       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
473       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
474       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
475       PetscCall(PetscDrawSetTitle(draw, title));
476 
477       /* TODO Get max and min only for this component */
478       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
479       if (!flg) {
480         PetscCall(VecMin(fv, NULL, &vbound[0]));
481         PetscCall(VecMax(fv, NULL, &vbound[1]));
482         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
483       }
484 
485       PetscCall(PetscDrawGetPopup(draw, &popup));
486       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
487       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
488       PetscCall(VecGetArrayRead(fv, &array));
489       for (c = cStart; c < cEnd; ++c) {
490         DMPolytopeType     ct;
491         PetscScalar       *coords = NULL, *a = NULL;
492         const PetscScalar *coords_arr;
493         PetscBool          isDG;
494         PetscInt           numCoords;
495         int                color[4] = {-1, -1, -1, -1};
496 
497         PetscCall(DMPlexGetCellType(dm, c, &ct));
498         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
499         if (a) {
500           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
501           color[1] = color[2] = color[3] = color[0];
502         } else {
503           PetscScalar *vals = NULL;
504           PetscInt     numVals, va;
505 
506           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
507           if (!numVals) {
508             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
509             continue;
510           }
511           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
512           switch (numVals / Nc) {
513           case 1: /* P1 Clamped Segment Prism */
514           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
515             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
516             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
517             break;
518           case 3: /* P1 Triangle */
519           case 4: /* P1 Quadrangle */
520             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
521             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
522             break;
523           case 6: /* P2 Triangle */
524           case 8: /* P2 Quadrangle */
525             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
526             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
527             break;
528           default:
529             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
530           }
531           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
532         }
533         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
534         switch (numCoords) {
535         case 6:
536         case 12: /* Localized triangle */
537           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
538           break;
539         case 8:
540         case 16: /* Localized quadrilateral */
541           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
542             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
543           } else {
544             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
545             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
546           }
547           break;
548         default:
549           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
550         }
551         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
552       }
553       PetscCall(VecRestoreArrayRead(fv, &array));
554       PetscCall(PetscDrawFlush(draw));
555       PetscCall(PetscDrawPause(draw));
556       PetscCall(PetscDrawSave(draw));
557     }
558     if (Nf > 1) {
559       PetscCall(VecRestoreSubVector(v, fis, &fv));
560       PetscCall(ISDestroy(&fis));
561       PetscCall(DMDestroy(&fdm));
562     }
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscDraw draw;
571   PetscInt  dim;
572   PetscBool isnull;
573 
574   PetscFunctionBegin;
575   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
576   PetscCall(PetscDrawIsNull(draw, &isnull));
577   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
578 
579   PetscCall(VecGetDM(v, &dm));
580   PetscCall(DMGetCoordinateDim(dm, &dim));
581   switch (dim) {
582   case 1:
583     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
584     break;
585   case 2:
586     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
587     break;
588   default:
589     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
590   }
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
595 {
596   DM                      dm;
597   Vec                     locv;
598   const char             *name;
599   PetscSection            section;
600   PetscInt                pStart, pEnd;
601   PetscInt                numFields;
602   PetscViewerVTKFieldType ft;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
607   PetscCall(PetscObjectGetName((PetscObject)v, &name));
608   PetscCall(PetscObjectSetName((PetscObject)locv, name));
609   PetscCall(VecCopy(v, locv));
610   PetscCall(DMGetLocalSection(dm, &section));
611   PetscCall(PetscSectionGetNumFields(section, &numFields));
612   if (!numFields) {
613     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
614     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
615   } else {
616     PetscInt f;
617 
618     for (f = 0; f < numFields; f++) {
619       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
620       if (ft == PETSC_VTK_INVALID) continue;
621       PetscCall(PetscObjectReference((PetscObject)locv));
622       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
623     }
624     PetscCall(VecDestroy(&locv));
625   }
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
641   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
642   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
643   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
644     PetscInt    i, numFields;
645     PetscObject fe;
646     PetscBool   fem  = PETSC_FALSE;
647     Vec         locv = v;
648     const char *name;
649     PetscInt    step;
650     PetscReal   time;
651 
652     PetscCall(DMGetNumFields(dm, &numFields));
653     for (i = 0; i < numFields; i++) {
654       PetscCall(DMGetField(dm, i, NULL, &fe));
655       if (fe->classid == PETSCFE_CLASSID) {
656         fem = PETSC_TRUE;
657         break;
658       }
659     }
660     if (fem) {
661       PetscObject isZero;
662 
663       PetscCall(DMGetLocalVector(dm, &locv));
664       PetscCall(PetscObjectGetName((PetscObject)v, &name));
665       PetscCall(PetscObjectSetName((PetscObject)locv, name));
666       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
667       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
668       PetscCall(VecCopy(v, locv));
669       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
670       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
671     }
672     if (isvtk) {
673       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
674     } else if (ishdf5) {
675 #if defined(PETSC_HAVE_HDF5)
676       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
677 #else
678       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680     } else if (isdraw) {
681       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
682     } else if (ispython) {
683       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
684     } else if (isglvis) {
685       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
686       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
687       PetscCall(VecView_GLVis(locv, viewer));
688     } else if (iscgns) {
689 #if defined(PETSC_HAVE_CGNS)
690       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
691 #else
692       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
693 #endif
694     }
695     if (fem) {
696       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
697       PetscCall(DMRestoreLocalVector(dm, &locv));
698     }
699   } else {
700     PetscBool isseq;
701 
702     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
703     if (isseq) PetscCall(VecView_Seq(v, viewer));
704     else PetscCall(VecView_MPI(v, viewer));
705   }
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
710 {
711   DM        dm;
712   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
713 
714   PetscFunctionBegin;
715   PetscCall(VecGetDM(v, &dm));
716   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
723   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
724   if (isvtk || isdraw || isglvis || iscgns || ispython) {
725     Vec         locv;
726     PetscObject isZero;
727     const char *name;
728 
729     PetscCall(DMGetLocalVector(dm, &locv));
730     PetscCall(PetscObjectGetName((PetscObject)v, &name));
731     PetscCall(PetscObjectSetName((PetscObject)locv, name));
732     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
733     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
734     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
735     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
736     PetscCall(VecView_Plex_Local(locv, viewer));
737     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
738     PetscCall(DMRestoreLocalVector(dm, &locv));
739   } else if (ishdf5) {
740 #if defined(PETSC_HAVE_HDF5)
741     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
742 #else
743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
744 #endif
745   } else if (isexodusii) {
746 #if defined(PETSC_HAVE_EXODUSII)
747     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
748 #else
749     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
750 #endif
751   } else {
752     PetscBool isseq;
753 
754     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
755     if (isseq) PetscCall(VecView_Seq(v, viewer));
756     else PetscCall(VecView_MPI(v, viewer));
757   }
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
762 {
763   DM                dm;
764   MPI_Comm          comm;
765   PetscViewerFormat format;
766   Vec               v;
767   PetscBool         isvtk, ishdf5;
768 
769   PetscFunctionBegin;
770   PetscCall(VecGetDM(originalv, &dm));
771   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
772   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
773   PetscCall(PetscViewerGetFormat(viewer, &format));
774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
776   if (format == PETSC_VIEWER_NATIVE) {
777     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
778     /* this need a better fix */
779     if (dm->useNatural) {
780       if (dm->sfNatural) {
781         const char *vecname;
782         PetscInt    n, nroots;
783 
784         PetscCall(VecGetLocalSize(originalv, &n));
785         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
786         if (n == nroots) {
787           PetscCall(DMPlexCreateNaturalVector(dm, &v));
788           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
789           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
790           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
791           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
792         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
793       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
794     } else v = originalv;
795   } else v = originalv;
796 
797   if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
800 #else
801     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
802 #endif
803   } else if (isvtk) {
804     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
805   } else {
806     PetscBool isseq;
807 
808     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
809     if (isseq) PetscCall(VecView_Seq(v, viewer));
810     else PetscCall(VecView_MPI(v, viewer));
811   }
812   if (v != originalv) PetscCall(VecDestroy(&v));
813   PetscFunctionReturn(PETSC_SUCCESS);
814 }
815 
816 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
817 {
818   DM        dm;
819   PetscBool ishdf5;
820 
821   PetscFunctionBegin;
822   PetscCall(VecGetDM(v, &dm));
823   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
824   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
825   if (ishdf5) {
826     DM          dmBC;
827     Vec         gv;
828     const char *name;
829 
830     PetscCall(DMGetOutputDM(dm, &dmBC));
831     PetscCall(DMGetGlobalVector(dmBC, &gv));
832     PetscCall(PetscObjectGetName((PetscObject)v, &name));
833     PetscCall(PetscObjectSetName((PetscObject)gv, name));
834     PetscCall(VecLoad_Default(gv, viewer));
835     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
836     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
837     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
838   } else PetscCall(VecLoad_Default(v, viewer));
839   PetscFunctionReturn(PETSC_SUCCESS);
840 }
841 
842 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
843 {
844   DM        dm;
845   PetscBool ishdf5, isexodusii, iscgns;
846 
847   PetscFunctionBegin;
848   PetscCall(VecGetDM(v, &dm));
849   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
850   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
851   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
852   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
853   if (ishdf5) {
854 #if defined(PETSC_HAVE_HDF5)
855     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
856 #else
857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
858 #endif
859   } else if (isexodusii) {
860 #if defined(PETSC_HAVE_EXODUSII)
861     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
862 #else
863     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
864 #endif
865   } else if (iscgns) {
866 #if defined(PETSC_HAVE_CGNS)
867     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
868 #else
869     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
870 #endif
871   } else PetscCall(VecLoad_Default(v, viewer));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
876 {
877   DM                dm;
878   PetscViewerFormat format;
879   PetscBool         ishdf5;
880 
881   PetscFunctionBegin;
882   PetscCall(VecGetDM(originalv, &dm));
883   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
884   PetscCall(PetscViewerGetFormat(viewer, &format));
885   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
886   if (format == PETSC_VIEWER_NATIVE) {
887     if (dm->useNatural) {
888       if (dm->sfNatural) {
889         if (ishdf5) {
890 #if defined(PETSC_HAVE_HDF5)
891           Vec         v;
892           const char *vecname;
893 
894           PetscCall(DMPlexCreateNaturalVector(dm, &v));
895           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
896           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
897           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
898           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
899           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
900           PetscCall(VecDestroy(&v));
901 #else
902           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
903 #endif
904         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
905       }
906     } else PetscCall(VecLoad_Default(originalv, viewer));
907   }
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
912 {
913   PetscSection       coordSection;
914   Vec                coordinates;
915   DMLabel            depthLabel, celltypeLabel;
916   const char        *name[4];
917   const PetscScalar *a;
918   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
919 
920   PetscFunctionBegin;
921   PetscCall(DMGetDimension(dm, &dim));
922   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
923   PetscCall(DMGetCoordinateSection(dm, &coordSection));
924   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
925   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
926   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
927   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
928   PetscCall(VecGetArrayRead(coordinates, &a));
929   name[0]       = "vertex";
930   name[1]       = "edge";
931   name[dim - 1] = "face";
932   name[dim]     = "cell";
933   for (c = cStart; c < cEnd; ++c) {
934     PetscInt *closure = NULL;
935     PetscInt  closureSize, cl, ct;
936 
937     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
938     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
939     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
940     PetscCall(PetscViewerASCIIPushTab(viewer));
941     for (cl = 0; cl < closureSize * 2; cl += 2) {
942       PetscInt point = closure[cl], depth, dof, off, d, p;
943 
944       if ((point < pStart) || (point >= pEnd)) continue;
945       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
946       if (!dof) continue;
947       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
948       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
949       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
950       for (p = 0; p < dof / dim; ++p) {
951         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
952         for (d = 0; d < dim; ++d) {
953           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
954           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
955         }
956         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
957       }
958       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
959     }
960     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
961     PetscCall(PetscViewerASCIIPopTab(viewer));
962   }
963   PetscCall(VecRestoreArrayRead(coordinates, &a));
964   PetscFunctionReturn(PETSC_SUCCESS);
965 }
966 
967 typedef enum {
968   CS_CARTESIAN,
969   CS_POLAR,
970   CS_CYLINDRICAL,
971   CS_SPHERICAL
972 } CoordSystem;
973 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
974 
975 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
976 {
977   PetscInt i;
978 
979   PetscFunctionBegin;
980   if (dim > 3) {
981     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
982   } else {
983     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
984 
985     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
986     switch (cs) {
987     case CS_CARTESIAN:
988       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
989       break;
990     case CS_POLAR:
991       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
992       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
993       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
994       break;
995     case CS_CYLINDRICAL:
996       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
997       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
998       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
999       trcoords[2] = coords[2];
1000       break;
1001     case CS_SPHERICAL:
1002       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
1003       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1004       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1005       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1006       break;
1007     }
1008     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1009   }
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1014 {
1015   DM_Plex          *mesh = (DM_Plex *)dm->data;
1016   DM                cdm, cdmCell;
1017   PetscSection      coordSection, coordSectionCell;
1018   Vec               coordinates, coordinatesCell;
1019   PetscViewerFormat format;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscViewerGetFormat(viewer, &format));
1023   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1024     const char *name;
1025     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1026     PetscInt    pStart, pEnd, p, numLabels, l;
1027     PetscMPIInt rank, size;
1028 
1029     PetscCall(DMGetCoordinateDM(dm, &cdm));
1030     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1031     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1032     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1033     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1034     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1035     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1036     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1037     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1039     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1040     PetscCall(DMGetDimension(dm, &dim));
1041     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1042     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1043     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1044     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1045     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1046     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1047     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1048     for (p = pStart; p < pEnd; ++p) {
1049       PetscInt dof, off, s;
1050 
1051       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1052       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1053       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1054     }
1055     PetscCall(PetscViewerFlush(viewer));
1056     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1057     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1058     for (p = pStart; p < pEnd; ++p) {
1059       PetscInt dof, off, c;
1060 
1061       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1062       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1063       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
1064     }
1065     PetscCall(PetscViewerFlush(viewer));
1066     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1067     if (coordSection && coordinates) {
1068       CoordSystem        cs = CS_CARTESIAN;
1069       const PetscScalar *array, *arrayCell = NULL;
1070       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1071       PetscMPIInt        rank;
1072       const char        *name;
1073 
1074       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1075       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1076       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1077       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1078       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1079       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1080       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1081       pStart = PetscMin(pvStart, pcStart);
1082       pEnd   = PetscMax(pvEnd, pcEnd);
1083       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1084       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1085       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1086       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1087 
1088       PetscCall(VecGetArrayRead(coordinates, &array));
1089       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1090       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1091       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1092       for (p = pStart; p < pEnd; ++p) {
1093         PetscInt dof, off;
1094 
1095         if (p >= pvStart && p < pvEnd) {
1096           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1097           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1098           if (dof) {
1099             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1100             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1101             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1102           }
1103         }
1104         if (cdmCell && p >= pcStart && p < pcEnd) {
1105           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1106           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1107           if (dof) {
1108             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1109             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1110             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1111           }
1112         }
1113       }
1114       PetscCall(PetscViewerFlush(viewer));
1115       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1116       PetscCall(VecRestoreArrayRead(coordinates, &array));
1117       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1118     }
1119     PetscCall(DMGetNumLabels(dm, &numLabels));
1120     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1121     for (l = 0; l < numLabels; ++l) {
1122       DMLabel     label;
1123       PetscBool   isdepth;
1124       const char *name;
1125 
1126       PetscCall(DMGetLabelName(dm, l, &name));
1127       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1128       if (isdepth) continue;
1129       PetscCall(DMGetLabel(dm, name, &label));
1130       PetscCall(DMLabelView(label, viewer));
1131     }
1132     if (size > 1) {
1133       PetscSF sf;
1134 
1135       PetscCall(DMGetPointSF(dm, &sf));
1136       PetscCall(PetscSFView(sf, viewer));
1137     }
1138     if (mesh->periodic.face_sfs)
1139       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1140     PetscCall(PetscViewerFlush(viewer));
1141   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1142     const char  *name, *color;
1143     const char  *defcolors[3]  = {"gray", "orange", "green"};
1144     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1145     char         lname[PETSC_MAX_PATH_LEN];
1146     PetscReal    scale      = 2.0;
1147     PetscReal    tikzscale  = 1.0;
1148     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1149     double       tcoords[3];
1150     PetscScalar *coords;
1151     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1152     PetscMPIInt  rank, size;
1153     char       **names, **colors, **lcolors;
1154     PetscBool    flg, lflg;
1155     PetscBT      wp = NULL;
1156     PetscInt     pEnd, pStart;
1157 
1158     PetscCall(DMGetCoordinateDM(dm, &cdm));
1159     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1160     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1161     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1162     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1163     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1164     PetscCall(DMGetDimension(dm, &dim));
1165     PetscCall(DMPlexGetDepth(dm, &depth));
1166     PetscCall(DMGetNumLabels(dm, &numLabels));
1167     numLabels  = PetscMax(numLabels, 10);
1168     numColors  = 10;
1169     numLColors = 10;
1170     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1171     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1172     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1173     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1174     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1175     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1176     n = 4;
1177     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1178     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1179     n = 4;
1180     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1181     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1182     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1183     if (!useLabels) numLabels = 0;
1184     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1185     if (!useColors) {
1186       numColors = 3;
1187       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1188     }
1189     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1190     if (!useColors) {
1191       numLColors = 4;
1192       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1193     }
1194     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1195     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1196     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1197     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1198     if (depth < dim) plotEdges = PETSC_FALSE;
1199     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1200 
1201     /* filter points with labelvalue != labeldefaultvalue */
1202     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1203     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1204     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1205     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1206     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1207     if (lflg) {
1208       DMLabel lbl;
1209 
1210       PetscCall(DMGetLabel(dm, lname, &lbl));
1211       if (lbl) {
1212         PetscInt val, defval;
1213 
1214         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1215         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1216         for (c = pStart; c < pEnd; c++) {
1217           PetscInt *closure = NULL;
1218           PetscInt  closureSize;
1219 
1220           PetscCall(DMLabelGetValue(lbl, c, &val));
1221           if (val == defval) continue;
1222 
1223           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229 
1230     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1231     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1232     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1233     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1234 \\documentclass[tikz]{standalone}\n\n\
1235 \\usepackage{pgflibraryshapes}\n\
1236 \\usetikzlibrary{backgrounds}\n\
1237 \\usetikzlibrary{arrows}\n\
1238 \\begin{document}\n"));
1239     if (size > 1) {
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1241       for (p = 0; p < size; ++p) {
1242         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1243         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1244       }
1245       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1246     }
1247     if (drawHasse) {
1248       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1249 
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1264       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1265       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1266     }
1267     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1268 
1269     /* Plot vertices */
1270     PetscCall(VecGetArray(coordinates, &coords));
1271     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1272     for (v = vStart; v < vEnd; ++v) {
1273       PetscInt  off, dof, d;
1274       PetscBool isLabeled = PETSC_FALSE;
1275 
1276       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1277       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1278       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1279       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1280       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1281       for (d = 0; d < dof; ++d) {
1282         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1283         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1284       }
1285       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1286       if (dim == 3) {
1287         PetscReal tmp = tcoords[1];
1288         tcoords[1]    = tcoords[2];
1289         tcoords[2]    = -tmp;
1290       }
1291       for (d = 0; d < dof; ++d) {
1292         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1293         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1294       }
1295       if (drawHasse) color = colors[0 % numColors];
1296       else color = colors[rank % numColors];
1297       for (l = 0; l < numLabels; ++l) {
1298         PetscInt val;
1299         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1300         if (val >= 0) {
1301           color     = lcolors[l % numLColors];
1302           isLabeled = PETSC_TRUE;
1303           break;
1304         }
1305       }
1306       if (drawNumbers[0]) {
1307         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1308       } else if (drawColors[0]) {
1309         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1310       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1311     }
1312     PetscCall(VecRestoreArray(coordinates, &coords));
1313     PetscCall(PetscViewerFlush(viewer));
1314     /* Plot edges */
1315     if (plotEdges) {
1316       PetscCall(VecGetArray(coordinates, &coords));
1317       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1318       for (e = eStart; e < eEnd; ++e) {
1319         const PetscInt *cone;
1320         PetscInt        coneSize, offA, offB, dof, d;
1321 
1322         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1323         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1324         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1325         PetscCall(DMPlexGetCone(dm, e, &cone));
1326         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1327         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1328         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1329         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1330         for (d = 0; d < dof; ++d) {
1331           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1332           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1333         }
1334         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1335         if (dim == 3) {
1336           PetscReal tmp = tcoords[1];
1337           tcoords[1]    = tcoords[2];
1338           tcoords[2]    = -tmp;
1339         }
1340         for (d = 0; d < dof; ++d) {
1341           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1342           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1343         }
1344         if (drawHasse) color = colors[1 % numColors];
1345         else color = colors[rank % numColors];
1346         for (l = 0; l < numLabels; ++l) {
1347           PetscInt val;
1348           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1349           if (val >= 0) {
1350             color = lcolors[l % numLColors];
1351             break;
1352           }
1353         }
1354         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1355       }
1356       PetscCall(VecRestoreArray(coordinates, &coords));
1357       PetscCall(PetscViewerFlush(viewer));
1358       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1359     }
1360     /* Plot cells */
1361     if (dim == 3 || !drawNumbers[1]) {
1362       for (e = eStart; e < eEnd; ++e) {
1363         const PetscInt *cone;
1364 
1365         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1366         color = colors[rank % numColors];
1367         for (l = 0; l < numLabels; ++l) {
1368           PetscInt val;
1369           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1370           if (val >= 0) {
1371             color = lcolors[l % numLColors];
1372             break;
1373           }
1374         }
1375         PetscCall(DMPlexGetCone(dm, e, &cone));
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1377       }
1378     } else {
1379       DMPolytopeType ct;
1380 
1381       /* Drawing a 2D polygon */
1382       for (c = cStart; c < cEnd; ++c) {
1383         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1384         PetscCall(DMPlexGetCellType(dm, c, &ct));
1385         if (DMPolytopeTypeIsHybrid(ct)) {
1386           const PetscInt *cone;
1387           PetscInt        coneSize, e;
1388 
1389           PetscCall(DMPlexGetCone(dm, c, &cone));
1390           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1391           for (e = 0; e < coneSize; ++e) {
1392             const PetscInt *econe;
1393 
1394             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1395             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1396           }
1397         } else {
1398           PetscInt *closure = NULL;
1399           PetscInt  closureSize, Nv = 0, v;
1400 
1401           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1402           for (p = 0; p < closureSize * 2; p += 2) {
1403             const PetscInt point = closure[p];
1404 
1405             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1406           }
1407           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1408           for (v = 0; v <= Nv; ++v) {
1409             const PetscInt vertex = closure[v % Nv];
1410 
1411             if (v > 0) {
1412               if (plotEdges) {
1413                 const PetscInt *edge;
1414                 PetscInt        endpoints[2], ne;
1415 
1416                 endpoints[0] = closure[v - 1];
1417                 endpoints[1] = vertex;
1418                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1419                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1420                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1421                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1422               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1423             }
1424             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1425           }
1426           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1427           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1428         }
1429       }
1430     }
1431     for (c = cStart; c < cEnd; ++c) {
1432       double             ccoords[3] = {0.0, 0.0, 0.0};
1433       PetscBool          isLabeled  = PETSC_FALSE;
1434       PetscScalar       *cellCoords = NULL;
1435       const PetscScalar *array;
1436       PetscInt           numCoords, cdim, d;
1437       PetscBool          isDG;
1438 
1439       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1440       PetscCall(DMGetCoordinateDim(dm, &cdim));
1441       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1442       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1443       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1444       for (p = 0; p < numCoords / cdim; ++p) {
1445         for (d = 0; d < cdim; ++d) {
1446           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1447           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1448         }
1449         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1450         if (cdim == 3) {
1451           PetscReal tmp = tcoords[1];
1452           tcoords[1]    = tcoords[2];
1453           tcoords[2]    = -tmp;
1454         }
1455         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1456       }
1457       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1458       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1459       for (d = 0; d < cdim; ++d) {
1460         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1461         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1462       }
1463       if (drawHasse) color = colors[depth % numColors];
1464       else color = colors[rank % numColors];
1465       for (l = 0; l < numLabels; ++l) {
1466         PetscInt val;
1467         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1468         if (val >= 0) {
1469           color     = lcolors[l % numLColors];
1470           isLabeled = PETSC_TRUE;
1471           break;
1472         }
1473       }
1474       if (drawNumbers[dim]) {
1475         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1476       } else if (drawColors[dim]) {
1477         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1478       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1479     }
1480     if (drawHasse) {
1481       int height = 0;
1482 
1483       color = colors[depth % numColors];
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1485       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1487       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1489 
1490       if (depth > 2) {
1491         color = colors[1 % numColors];
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1496         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1497       }
1498 
1499       color = colors[1 % numColors];
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1501       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1502       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1503       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1504       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1505 
1506       color = colors[0 % numColors];
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1508       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1509       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1510       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1511       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1512 
1513       for (p = pStart; p < pEnd; ++p) {
1514         const PetscInt *cone;
1515         PetscInt        coneSize, cp;
1516 
1517         PetscCall(DMPlexGetCone(dm, p, &cone));
1518         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1519         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1520       }
1521     }
1522     PetscCall(PetscViewerFlush(viewer));
1523     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1524     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1526     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1527     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1528     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1529     PetscCall(PetscFree3(names, colors, lcolors));
1530     PetscCall(PetscBTDestroy(&wp));
1531   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1532     Vec                    cown, acown;
1533     VecScatter             sct;
1534     ISLocalToGlobalMapping g2l;
1535     IS                     gid, acis;
1536     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1537     MPI_Group              ggroup, ngroup;
1538     PetscScalar           *array, nid;
1539     const PetscInt        *idxs;
1540     PetscInt              *idxs2, *start, *adjacency, *work;
1541     PetscInt64             lm[3], gm[3];
1542     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1543     PetscMPIInt            d1, d2, rank;
1544 
1545     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1546     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1547 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1548     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1549 #endif
1550     if (ncomm != MPI_COMM_NULL) {
1551       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1552       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1553       d1 = 0;
1554       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1555       nid = d2;
1556       PetscCallMPI(MPI_Group_free(&ggroup));
1557       PetscCallMPI(MPI_Group_free(&ngroup));
1558       PetscCallMPI(MPI_Comm_free(&ncomm));
1559     } else nid = 0.0;
1560 
1561     /* Get connectivity */
1562     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1563     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1564 
1565     /* filter overlapped local cells */
1566     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1567     PetscCall(ISGetIndices(gid, &idxs));
1568     PetscCall(ISGetLocalSize(gid, &cum));
1569     PetscCall(PetscMalloc1(cum, &idxs2));
1570     for (c = cStart, cum = 0; c < cEnd; c++) {
1571       if (idxs[c - cStart] < 0) continue;
1572       idxs2[cum++] = idxs[c - cStart];
1573     }
1574     PetscCall(ISRestoreIndices(gid, &idxs));
1575     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1576     PetscCall(ISDestroy(&gid));
1577     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1578 
1579     /* support for node-aware cell locality */
1580     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1581     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1582     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1583     PetscCall(VecGetArray(cown, &array));
1584     for (c = 0; c < numVertices; c++) array[c] = nid;
1585     PetscCall(VecRestoreArray(cown, &array));
1586     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1587     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1588     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1589     PetscCall(ISDestroy(&acis));
1590     PetscCall(VecScatterDestroy(&sct));
1591     PetscCall(VecDestroy(&cown));
1592 
1593     /* compute edgeCut */
1594     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1595     PetscCall(PetscMalloc1(cum, &work));
1596     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1597     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1598     PetscCall(ISDestroy(&gid));
1599     PetscCall(VecGetArray(acown, &array));
1600     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1601       PetscInt totl;
1602 
1603       totl = start[c + 1] - start[c];
1604       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1605       for (i = 0; i < totl; i++) {
1606         if (work[i] < 0) {
1607           ect += 1;
1608           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1609         }
1610       }
1611     }
1612     PetscCall(PetscFree(work));
1613     PetscCall(VecRestoreArray(acown, &array));
1614     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1615     lm[1] = -numVertices;
1616     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1617     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1618     lm[0] = ect;                     /* edgeCut */
1619     lm[1] = ectn;                    /* node-aware edgeCut */
1620     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1621     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1622     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1623 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1624     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1625 #else
1626     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1627 #endif
1628     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1629     PetscCall(PetscFree(start));
1630     PetscCall(PetscFree(adjacency));
1631     PetscCall(VecDestroy(&acown));
1632   } else {
1633     const char    *name;
1634     PetscInt      *sizes, *hybsizes, *ghostsizes;
1635     PetscInt       locDepth, depth, cellHeight, dim, d;
1636     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1637     PetscInt       numLabels, l, maxSize = 17;
1638     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1639     MPI_Comm       comm;
1640     PetscMPIInt    size, rank;
1641 
1642     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1643     PetscCallMPI(MPI_Comm_size(comm, &size));
1644     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1645     PetscCall(DMGetDimension(dm, &dim));
1646     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1647     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1648     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1649     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1650     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1651     PetscCall(DMPlexGetDepth(dm, &locDepth));
1652     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1653     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1654     gcNum = gcEnd - gcStart;
1655     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1656     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1657     for (d = 0; d <= depth; d++) {
1658       PetscInt Nc[2] = {0, 0}, ict;
1659 
1660       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1661       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1662       ict = ct0;
1663       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1664       ct0 = (DMPolytopeType)ict;
1665       for (p = pStart; p < pEnd; ++p) {
1666         DMPolytopeType ct;
1667 
1668         PetscCall(DMPlexGetCellType(dm, p, &ct));
1669         if (ct == ct0) ++Nc[0];
1670         else ++Nc[1];
1671       }
1672       if (size < maxSize) {
1673         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1674         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1675         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1676         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1677         for (p = 0; p < size; ++p) {
1678           if (rank == 0) {
1679             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1680             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1681             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1682           }
1683         }
1684       } else {
1685         PetscInt locMinMax[2];
1686 
1687         locMinMax[0] = Nc[0] + Nc[1];
1688         locMinMax[1] = Nc[0] + Nc[1];
1689         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1690         locMinMax[0] = Nc[1];
1691         locMinMax[1] = Nc[1];
1692         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1693         if (d == depth) {
1694           locMinMax[0] = gcNum;
1695           locMinMax[1] = gcNum;
1696           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1697         }
1698         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1699         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1700         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1701         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1702       }
1703       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1704     }
1705     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1706     {
1707       const PetscReal *maxCell;
1708       const PetscReal *L;
1709       PetscBool        localized;
1710 
1711       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1712       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1713       if (L || localized) {
1714         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1715         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1716         if (L) {
1717           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1718           for (d = 0; d < dim; ++d) {
1719             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1720             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1721           }
1722           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1723         }
1724         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1725         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1726       }
1727     }
1728     PetscCall(DMGetNumLabels(dm, &numLabels));
1729     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1730     for (l = 0; l < numLabels; ++l) {
1731       DMLabel     label;
1732       const char *name;
1733       PetscInt   *values;
1734       PetscInt    numValues, v;
1735 
1736       PetscCall(DMGetLabelName(dm, l, &name));
1737       PetscCall(DMGetLabel(dm, name, &label));
1738       PetscCall(DMLabelGetNumValues(label, &numValues));
1739       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1740 
1741       { // Extract array of DMLabel values so it can be sorted
1742         IS              is_values;
1743         const PetscInt *is_values_local = NULL;
1744 
1745         PetscCall(DMLabelGetValueIS(label, &is_values));
1746         PetscCall(ISGetIndices(is_values, &is_values_local));
1747         PetscCall(PetscMalloc1(numValues, &values));
1748         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1749         PetscCall(PetscSortInt(numValues, values));
1750         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1751         PetscCall(ISDestroy(&is_values));
1752       }
1753       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1754       for (v = 0; v < numValues; ++v) {
1755         PetscInt size;
1756 
1757         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1758         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1759         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1760       }
1761       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1762       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1763       PetscCall(PetscFree(values));
1764     }
1765     {
1766       char    **labelNames;
1767       PetscInt  Nl = numLabels;
1768       PetscBool flg;
1769 
1770       PetscCall(PetscMalloc1(Nl, &labelNames));
1771       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1772       for (l = 0; l < Nl; ++l) {
1773         DMLabel label;
1774 
1775         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1776         if (flg) {
1777           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1778           PetscCall(DMLabelView(label, viewer));
1779         }
1780         PetscCall(PetscFree(labelNames[l]));
1781       }
1782       PetscCall(PetscFree(labelNames));
1783     }
1784     /* If no fields are specified, people do not want to see adjacency */
1785     if (dm->Nf) {
1786       PetscInt f;
1787 
1788       for (f = 0; f < dm->Nf; ++f) {
1789         const char *name;
1790 
1791         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1792         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1793         PetscCall(PetscViewerASCIIPushTab(viewer));
1794         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1795         if (dm->fields[f].adjacency[0]) {
1796           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1797           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1798         } else {
1799           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1800           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1801         }
1802         PetscCall(PetscViewerASCIIPopTab(viewer));
1803       }
1804     }
1805     PetscCall(DMGetCoarseDM(dm, &cdm));
1806     if (cdm) {
1807       PetscCall(PetscViewerASCIIPushTab(viewer));
1808       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1809       PetscCall(DMPlexView_Ascii(cdm, viewer));
1810       PetscCall(PetscViewerASCIIPopTab(viewer));
1811     }
1812   }
1813   PetscFunctionReturn(PETSC_SUCCESS);
1814 }
1815 
1816 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1817 {
1818   DMPolytopeType ct;
1819   PetscMPIInt    rank;
1820   PetscInt       cdim;
1821 
1822   PetscFunctionBegin;
1823   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1824   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1825   PetscCall(DMGetCoordinateDim(dm, &cdim));
1826   switch (ct) {
1827   case DM_POLYTOPE_SEGMENT:
1828   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1829     switch (cdim) {
1830     case 1: {
1831       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1832       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1833 
1834       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1835       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1836       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1837     } break;
1838     case 2: {
1839       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1840       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1841       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1842 
1843       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1844       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1845       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1846     } break;
1847     default:
1848       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1849     }
1850     break;
1851   case DM_POLYTOPE_TRIANGLE:
1852     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1853     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1854     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1855     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1856     break;
1857   case DM_POLYTOPE_QUADRILATERAL:
1858     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1859     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1860     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1861     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1862     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1863     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1864     break;
1865   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1866     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1867     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1868     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1869     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1870     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1871     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1872     break;
1873   case DM_POLYTOPE_FV_GHOST:
1874     break;
1875   default:
1876     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1877   }
1878   PetscFunctionReturn(PETSC_SUCCESS);
1879 }
1880 
1881 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1882 {
1883   PetscReal   centroid[2] = {0., 0.};
1884   PetscMPIInt rank;
1885   PetscMPIInt fillColor;
1886 
1887   PetscFunctionBegin;
1888   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1889   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1890   for (PetscInt v = 0; v < Nv; ++v) {
1891     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1892     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1893   }
1894   for (PetscInt e = 0; e < Nv; ++e) {
1895     refCoords[0] = refVertices[e * 2 + 0];
1896     refCoords[1] = refVertices[e * 2 + 1];
1897     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1898       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1899       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1900     }
1901     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1902     for (PetscInt d = 0; d < edgeDiv; ++d) {
1903       PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1904       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1905     }
1906   }
1907   PetscFunctionReturn(PETSC_SUCCESS);
1908 }
1909 
1910 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1911 {
1912   DMPolytopeType ct;
1913 
1914   PetscFunctionBegin;
1915   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1916   switch (ct) {
1917   case DM_POLYTOPE_TRIANGLE: {
1918     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1919 
1920     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1921   } break;
1922   case DM_POLYTOPE_QUADRILATERAL: {
1923     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1924 
1925     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1926   } break;
1927   default:
1928     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1929   }
1930   PetscFunctionReturn(PETSC_SUCCESS);
1931 }
1932 
1933 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1934 {
1935   PetscDraw    draw;
1936   DM           cdm;
1937   PetscSection coordSection;
1938   Vec          coordinates;
1939   PetscReal    xyl[3], xyr[3];
1940   PetscReal   *refCoords, *edgeCoords;
1941   PetscBool    isnull, drawAffine;
1942   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1943 
1944   PetscFunctionBegin;
1945   PetscCall(DMGetCoordinateDim(dm, &dim));
1946   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1947   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1948   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1949   edgeDiv    = cDegree + 1;
1950   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1951   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1952   PetscCall(DMGetCoordinateDM(dm, &cdm));
1953   PetscCall(DMGetLocalSection(cdm, &coordSection));
1954   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1955   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1956   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1957 
1958   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1959   PetscCall(PetscDrawIsNull(draw, &isnull));
1960   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1961   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1962 
1963   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1964   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1965   PetscCall(PetscDrawClear(draw));
1966 
1967   for (c = cStart; c < cEnd; ++c) {
1968     PetscScalar       *coords = NULL;
1969     const PetscScalar *coords_arr;
1970     PetscInt           numCoords;
1971     PetscBool          isDG;
1972 
1973     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1974     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1975     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1976     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1977   }
1978   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1979   PetscCall(PetscDrawFlush(draw));
1980   PetscCall(PetscDrawPause(draw));
1981   PetscCall(PetscDrawSave(draw));
1982   PetscFunctionReturn(PETSC_SUCCESS);
1983 }
1984 
1985 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1986 {
1987   DM           odm = dm, rdm = dm, cdm;
1988   PetscFE      fe;
1989   PetscSpace   sp;
1990   PetscClassId id;
1991   PetscInt     degree;
1992   PetscBool    hoView = PETSC_TRUE;
1993 
1994   PetscFunctionBegin;
1995   PetscObjectOptionsBegin((PetscObject)dm);
1996   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1997   PetscOptionsEnd();
1998   PetscCall(PetscObjectReference((PetscObject)dm));
1999   *hdm = dm;
2000   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2001   PetscCall(DMGetCoordinateDM(dm, &cdm));
2002   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2003   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2004   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2005   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2006   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2007   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2008     DM  cdm, rcdm;
2009     Mat In;
2010     Vec cl, rcl;
2011 
2012     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2013     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
2014     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2015     PetscCall(DMGetCoordinateDM(odm, &cdm));
2016     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2017     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2018     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2019     PetscCall(DMSetCoarseDM(rcdm, cdm));
2020     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2021     PetscCall(MatMult(In, cl, rcl));
2022     PetscCall(MatDestroy(&In));
2023     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2024     PetscCall(DMDestroy(&odm));
2025     odm = rdm;
2026   }
2027   *hdm = rdm;
2028   PetscFunctionReturn(PETSC_SUCCESS);
2029 }
2030 
2031 #if defined(PETSC_HAVE_EXODUSII)
2032   #include <exodusII.h>
2033   #include <petscviewerexodusii.h>
2034 #endif
2035 
2036 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2037 {
2038   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2039   char      name[PETSC_MAX_PATH_LEN];
2040 
2041   PetscFunctionBegin;
2042   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2043   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2044   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2045   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2046   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2047   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2048   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2050   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2051   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2052   if (iascii) {
2053     PetscViewerFormat format;
2054     PetscCall(PetscViewerGetFormat(viewer, &format));
2055     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2056     else PetscCall(DMPlexView_Ascii(dm, viewer));
2057   } else if (ishdf5) {
2058 #if defined(PETSC_HAVE_HDF5)
2059     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2060 #else
2061     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2062 #endif
2063   } else if (isvtk) {
2064     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2065   } else if (isdraw) {
2066     DM hdm;
2067 
2068     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2069     PetscCall(DMPlexView_Draw(hdm, viewer));
2070     PetscCall(DMDestroy(&hdm));
2071   } else if (isglvis) {
2072     PetscCall(DMPlexView_GLVis(dm, viewer));
2073 #if defined(PETSC_HAVE_EXODUSII)
2074   } else if (isexodus) {
2075     /*
2076       exodusII requires that all sets be part of exactly one cell set.
2077       If the dm does not have a "Cell Sets" label defined, we create one
2078       with ID 1, containing all cells.
2079       Note that if the Cell Sets label is defined but does not cover all cells,
2080       we may still have a problem. This should probably be checked here or in the viewer;
2081     */
2082     PetscInt numCS;
2083     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2084     if (!numCS) {
2085       PetscInt cStart, cEnd, c;
2086       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2087       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2088       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2089     }
2090     PetscCall(DMView_PlexExodusII(dm, viewer));
2091 #endif
2092 #if defined(PETSC_HAVE_CGNS)
2093   } else if (iscgns) {
2094     PetscCall(DMView_PlexCGNS(dm, viewer));
2095 #endif
2096   } else if (ispython) {
2097     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2098   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2099   /* Optionally view the partition */
2100   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2101   if (flg) {
2102     Vec ranks;
2103     PetscCall(DMPlexCreateRankField(dm, &ranks));
2104     PetscCall(VecView(ranks, viewer));
2105     PetscCall(VecDestroy(&ranks));
2106   }
2107   /* Optionally view a label */
2108   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2109   if (flg) {
2110     DMLabel label;
2111     Vec     val;
2112 
2113     PetscCall(DMGetLabel(dm, name, &label));
2114     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2115     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2116     PetscCall(VecView(val, viewer));
2117     PetscCall(VecDestroy(&val));
2118   }
2119   PetscFunctionReturn(PETSC_SUCCESS);
2120 }
2121 
2122 /*@
2123   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2124 
2125   Collective
2126 
2127   Input Parameters:
2128 + dm     - The `DM` whose topology is to be saved
2129 - viewer - The `PetscViewer` to save it in
2130 
2131   Level: advanced
2132 
2133 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2134 @*/
2135 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2136 {
2137   PetscBool ishdf5;
2138 
2139   PetscFunctionBegin;
2140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2141   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2142   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2143   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2144   if (ishdf5) {
2145 #if defined(PETSC_HAVE_HDF5)
2146     PetscViewerFormat format;
2147     PetscCall(PetscViewerGetFormat(viewer, &format));
2148     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2149       IS globalPointNumbering;
2150 
2151       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2152       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2153       PetscCall(ISDestroy(&globalPointNumbering));
2154     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   }
2159   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2160   PetscFunctionReturn(PETSC_SUCCESS);
2161 }
2162 
2163 /*@
2164   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2165 
2166   Collective
2167 
2168   Input Parameters:
2169 + dm     - The `DM` whose coordinates are to be saved
2170 - viewer - The `PetscViewer` for saving
2171 
2172   Level: advanced
2173 
2174 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2175 @*/
2176 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2177 {
2178   PetscBool ishdf5;
2179 
2180   PetscFunctionBegin;
2181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2182   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2183   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2184   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2185   if (ishdf5) {
2186 #if defined(PETSC_HAVE_HDF5)
2187     PetscViewerFormat format;
2188     PetscCall(PetscViewerGetFormat(viewer, &format));
2189     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2190       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2191     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2192 #else
2193     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2194 #endif
2195   }
2196   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2197   PetscFunctionReturn(PETSC_SUCCESS);
2198 }
2199 
2200 /*@
2201   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2202 
2203   Collective
2204 
2205   Input Parameters:
2206 + dm     - The `DM` whose labels are to be saved
2207 - viewer - The `PetscViewer` for saving
2208 
2209   Level: advanced
2210 
2211 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2212 @*/
2213 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2214 {
2215   PetscBool ishdf5;
2216 
2217   PetscFunctionBegin;
2218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2219   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2220   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2221   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2222   if (ishdf5) {
2223 #if defined(PETSC_HAVE_HDF5)
2224     IS                globalPointNumbering;
2225     PetscViewerFormat format;
2226 
2227     PetscCall(PetscViewerGetFormat(viewer, &format));
2228     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2229       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2230       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2231       PetscCall(ISDestroy(&globalPointNumbering));
2232     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2233 #else
2234     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2235 #endif
2236   }
2237   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2238   PetscFunctionReturn(PETSC_SUCCESS);
2239 }
2240 
2241 /*@
2242   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2243 
2244   Collective
2245 
2246   Input Parameters:
2247 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2248 . viewer    - The `PetscViewer` for saving
2249 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2250 
2251   Level: advanced
2252 
2253   Notes:
2254   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (`dm`) points. When the topology (`dm`) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
2255 
2256   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2259 @*/
2260 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2261 {
2262   PetscBool ishdf5;
2263 
2264   PetscFunctionBegin;
2265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2266   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2267   if (!sectiondm) sectiondm = dm;
2268   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2274 #else
2275     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2276 #endif
2277   }
2278   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2279   PetscFunctionReturn(PETSC_SUCCESS);
2280 }
2281 
2282 /*@
2283   DMPlexGlobalVectorView - Saves a global vector
2284 
2285   Collective
2286 
2287   Input Parameters:
2288 + dm        - The `DM` that represents the topology
2289 . viewer    - The `PetscViewer` to save data with
2290 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2291 - vec       - The global vector to be saved
2292 
2293   Level: advanced
2294 
2295   Notes:
2296   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2297 
2298   Calling sequence:
2299 .vb
2300        DMCreate(PETSC_COMM_WORLD, &dm);
2301        DMSetType(dm, DMPLEX);
2302        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2303        DMClone(dm, &sectiondm);
2304        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2305        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2306        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2307        PetscSectionSetChart(section, pStart, pEnd);
2308        PetscSectionSetUp(section);
2309        DMSetLocalSection(sectiondm, section);
2310        PetscSectionDestroy(&section);
2311        DMGetGlobalVector(sectiondm, &vec);
2312        PetscObjectSetName((PetscObject)vec, "vec_name");
2313        DMPlexTopologyView(dm, viewer);
2314        DMPlexSectionView(dm, viewer, sectiondm);
2315        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2316        DMRestoreGlobalVector(sectiondm, &vec);
2317        DMDestroy(&sectiondm);
2318        DMDestroy(&dm);
2319 .ve
2320 
2321 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2322 @*/
2323 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2324 {
2325   PetscBool ishdf5;
2326 
2327   PetscFunctionBegin;
2328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2329   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2330   if (!sectiondm) sectiondm = dm;
2331   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2332   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2333   /* Check consistency */
2334   {
2335     PetscSection section;
2336     PetscBool    includesConstraints;
2337     PetscInt     m, m1;
2338 
2339     PetscCall(VecGetLocalSize(vec, &m1));
2340     PetscCall(DMGetGlobalSection(sectiondm, &section));
2341     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2342     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2343     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2344     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2345   }
2346   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2347   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2348   if (ishdf5) {
2349 #if defined(PETSC_HAVE_HDF5)
2350     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2351 #else
2352     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2353 #endif
2354   }
2355   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2356   PetscFunctionReturn(PETSC_SUCCESS);
2357 }
2358 
2359 /*@
2360   DMPlexLocalVectorView - Saves a local vector
2361 
2362   Collective
2363 
2364   Input Parameters:
2365 + dm        - The `DM` that represents the topology
2366 . viewer    - The `PetscViewer` to save data with
2367 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2368 - vec       - The local vector to be saved
2369 
2370   Level: advanced
2371 
2372   Note:
2373   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2374 
2375   Calling sequence:
2376 .vb
2377        DMCreate(PETSC_COMM_WORLD, &dm);
2378        DMSetType(dm, DMPLEX);
2379        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2380        DMClone(dm, &sectiondm);
2381        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2382        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2383        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2384        PetscSectionSetChart(section, pStart, pEnd);
2385        PetscSectionSetUp(section);
2386        DMSetLocalSection(sectiondm, section);
2387        DMGetLocalVector(sectiondm, &vec);
2388        PetscObjectSetName((PetscObject)vec, "vec_name");
2389        DMPlexTopologyView(dm, viewer);
2390        DMPlexSectionView(dm, viewer, sectiondm);
2391        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2392        DMRestoreLocalVector(sectiondm, &vec);
2393        DMDestroy(&sectiondm);
2394        DMDestroy(&dm);
2395 .ve
2396 
2397 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2398 @*/
2399 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   if (!sectiondm) sectiondm = dm;
2407   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetLocalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2436 {
2437   PetscBool ishdf5;
2438 
2439   PetscFunctionBegin;
2440   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2441   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2442   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2443   if (ishdf5) {
2444 #if defined(PETSC_HAVE_HDF5)
2445     PetscViewerFormat format;
2446     PetscCall(PetscViewerGetFormat(viewer, &format));
2447     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2448       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2449     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2450       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2451     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2452     PetscFunctionReturn(PETSC_SUCCESS);
2453 #else
2454     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2455 #endif
2456   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2457 }
2458 
2459 /*@
2460   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2461 
2462   Collective
2463 
2464   Input Parameters:
2465 + dm     - The `DM` into which the topology is loaded
2466 - viewer - The `PetscViewer` for the saved topology
2467 
2468   Output Parameter:
2469 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points;
2470   `NULL` if unneeded
2471 
2472   Level: advanced
2473 
2474 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2475           `PetscViewer`, `PetscSF`
2476 @*/
2477 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2478 {
2479   PetscBool ishdf5;
2480 
2481   PetscFunctionBegin;
2482   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2483   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2484   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2485   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2486   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2487   if (ishdf5) {
2488 #if defined(PETSC_HAVE_HDF5)
2489     PetscViewerFormat format;
2490     PetscCall(PetscViewerGetFormat(viewer, &format));
2491     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2492       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2493     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2494 #else
2495     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2496 #endif
2497   }
2498   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2499   PetscFunctionReturn(PETSC_SUCCESS);
2500 }
2501 
2502 /*@
2503   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2504 
2505   Collective
2506 
2507   Input Parameters:
2508 + dm                   - The `DM` into which the coordinates are loaded
2509 . viewer               - The `PetscViewer` for the saved coordinates
2510 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2511 
2512   Level: advanced
2513 
2514 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2515           `PetscSF`, `PetscViewer`
2516 @*/
2517 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2518 {
2519   PetscBool ishdf5;
2520 
2521   PetscFunctionBegin;
2522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2523   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2524   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2525   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2526   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2527   if (ishdf5) {
2528 #if defined(PETSC_HAVE_HDF5)
2529     PetscViewerFormat format;
2530     PetscCall(PetscViewerGetFormat(viewer, &format));
2531     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2532       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2533     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2534 #else
2535     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2536 #endif
2537   }
2538   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2539   PetscFunctionReturn(PETSC_SUCCESS);
2540 }
2541 
2542 /*@
2543   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2544 
2545   Collective
2546 
2547   Input Parameters:
2548 + dm                   - The `DM` into which the labels are loaded
2549 . viewer               - The `PetscViewer` for the saved labels
2550 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2551 
2552   Level: advanced
2553 
2554   Note:
2555   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2556 
2557 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2558           `PetscSF`, `PetscViewer`
2559 @*/
2560 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2561 {
2562   PetscBool ishdf5;
2563 
2564   PetscFunctionBegin;
2565   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2566   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2567   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2568   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2569   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2570   if (ishdf5) {
2571 #if defined(PETSC_HAVE_HDF5)
2572     PetscViewerFormat format;
2573 
2574     PetscCall(PetscViewerGetFormat(viewer, &format));
2575     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2576       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2577     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2578 #else
2579     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2580 #endif
2581   }
2582   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2583   PetscFunctionReturn(PETSC_SUCCESS);
2584 }
2585 
2586 /*@
2587   DMPlexSectionLoad - Loads section into a `DMPLEX`
2588 
2589   Collective
2590 
2591   Input Parameters:
2592 + dm                   - The `DM` that represents the topology
2593 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2594 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2595 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2596 
2597   Output Parameters:
2598 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2599 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2600 
2601   Level: advanced
2602 
2603   Notes:
2604   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2605 
2606   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2607 
2608   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2609 
2610   Example using 2 processes:
2611 .vb
2612   NX (number of points on dm): 4
2613   sectionA                   : the on-disk section
2614   vecA                       : a vector associated with sectionA
2615   sectionB                   : sectiondm's local section constructed in this function
2616   vecB (local)               : a vector associated with sectiondm's local section
2617   vecB (global)              : a vector associated with sectiondm's global section
2618 
2619                                      rank 0    rank 1
2620   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2621   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2622   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2623   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2624   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2625   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2626   sectionB->atlasDof             :     1 0 1 | 1 3
2627   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2628   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2629   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2630 .ve
2631   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2632 
2633 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2634 @*/
2635 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2636 {
2637   PetscBool ishdf5;
2638 
2639   PetscFunctionBegin;
2640   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2641   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2642   if (!sectiondm) sectiondm = dm;
2643   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2644   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2645   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2646   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2647   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2648   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2649   if (ishdf5) {
2650 #if defined(PETSC_HAVE_HDF5)
2651     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2652 #else
2653     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2654 #endif
2655   }
2656   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2657   PetscFunctionReturn(PETSC_SUCCESS);
2658 }
2659 
2660 /*@
2661   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2662 
2663   Collective
2664 
2665   Input Parameters:
2666 + dm        - The `DM` that represents the topology
2667 . viewer    - The `PetscViewer` that represents the on-disk vector data
2668 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2669 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2670 - vec       - The global vector to set values of
2671 
2672   Level: advanced
2673 
2674   Notes:
2675   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2676 
2677   Calling sequence:
2678 .vb
2679        DMCreate(PETSC_COMM_WORLD, &dm);
2680        DMSetType(dm, DMPLEX);
2681        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2682        DMPlexTopologyLoad(dm, viewer, &sfX);
2683        DMClone(dm, &sectiondm);
2684        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2685        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2686        DMGetGlobalVector(sectiondm, &vec);
2687        PetscObjectSetName((PetscObject)vec, "vec_name");
2688        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2689        DMRestoreGlobalVector(sectiondm, &vec);
2690        PetscSFDestroy(&gsf);
2691        PetscSFDestroy(&sfX);
2692        DMDestroy(&sectiondm);
2693        DMDestroy(&dm);
2694 .ve
2695 
2696 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2697           `PetscSF`, `PetscViewer`
2698 @*/
2699 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2700 {
2701   PetscBool ishdf5;
2702 
2703   PetscFunctionBegin;
2704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2705   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2706   if (!sectiondm) sectiondm = dm;
2707   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2708   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2709   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2710   /* Check consistency */
2711   {
2712     PetscSection section;
2713     PetscBool    includesConstraints;
2714     PetscInt     m, m1;
2715 
2716     PetscCall(VecGetLocalSize(vec, &m1));
2717     PetscCall(DMGetGlobalSection(sectiondm, &section));
2718     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2719     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2720     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2721     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2722   }
2723   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2724   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2725   if (ishdf5) {
2726 #if defined(PETSC_HAVE_HDF5)
2727     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2728 #else
2729     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2730 #endif
2731   }
2732   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2733   PetscFunctionReturn(PETSC_SUCCESS);
2734 }
2735 
2736 /*@
2737   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2738 
2739   Collective
2740 
2741   Input Parameters:
2742 + dm        - The `DM` that represents the topology
2743 . viewer    - The `PetscViewer` that represents the on-disk vector data
2744 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2745 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2746 - vec       - The local vector to set values of
2747 
2748   Level: advanced
2749 
2750   Notes:
2751   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2752 
2753   Calling sequence:
2754 .vb
2755        DMCreate(PETSC_COMM_WORLD, &dm);
2756        DMSetType(dm, DMPLEX);
2757        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2758        DMPlexTopologyLoad(dm, viewer, &sfX);
2759        DMClone(dm, &sectiondm);
2760        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2761        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2762        DMGetLocalVector(sectiondm, &vec);
2763        PetscObjectSetName((PetscObject)vec, "vec_name");
2764        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2765        DMRestoreLocalVector(sectiondm, &vec);
2766        PetscSFDestroy(&lsf);
2767        PetscSFDestroy(&sfX);
2768        DMDestroy(&sectiondm);
2769        DMDestroy(&dm);
2770 .ve
2771 
2772 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2773           `PetscSF`, `PetscViewer`
2774 @*/
2775 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2776 {
2777   PetscBool ishdf5;
2778 
2779   PetscFunctionBegin;
2780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2781   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2782   if (!sectiondm) sectiondm = dm;
2783   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2784   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2785   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2786   /* Check consistency */
2787   {
2788     PetscSection section;
2789     PetscBool    includesConstraints;
2790     PetscInt     m, m1;
2791 
2792     PetscCall(VecGetLocalSize(vec, &m1));
2793     PetscCall(DMGetLocalSection(sectiondm, &section));
2794     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2795     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2796     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2797     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2798   }
2799   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2800   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2801   if (ishdf5) {
2802 #if defined(PETSC_HAVE_HDF5)
2803     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2804 #else
2805     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2806 #endif
2807   }
2808   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2809   PetscFunctionReturn(PETSC_SUCCESS);
2810 }
2811 
2812 PetscErrorCode DMDestroy_Plex(DM dm)
2813 {
2814   DM_Plex *mesh = (DM_Plex *)dm->data;
2815 
2816   PetscFunctionBegin;
2817   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2818   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2819   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2820   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2821   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2822   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2823   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2824   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2825   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2826   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2827   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2828   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2829   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2830   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2837   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2838   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2839   PetscCall(PetscFree(mesh->cones));
2840   PetscCall(PetscFree(mesh->coneOrientations));
2841   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2842   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2843   PetscCall(PetscFree(mesh->supports));
2844   PetscCall(PetscFree(mesh->cellTypes));
2845   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2846   PetscCall(PetscFree(mesh->tetgenOpts));
2847   PetscCall(PetscFree(mesh->triangleOpts));
2848   PetscCall(PetscFree(mesh->transformType));
2849   PetscCall(PetscFree(mesh->distributionName));
2850   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2851   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2852   PetscCall(ISDestroy(&mesh->subpointIS));
2853   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2854   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2855   if (mesh->periodic.face_sfs) {
2856     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2857     PetscCall(PetscFree(mesh->periodic.face_sfs));
2858   }
2859   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2860   if (mesh->periodic.periodic_points) {
2861     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2862     PetscCall(PetscFree(mesh->periodic.periodic_points));
2863   }
2864   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2865   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2866   PetscCall(ISDestroy(&mesh->anchorIS));
2867   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2868   PetscCall(PetscFree(mesh->parents));
2869   PetscCall(PetscFree(mesh->childIDs));
2870   PetscCall(PetscSectionDestroy(&mesh->childSection));
2871   PetscCall(PetscFree(mesh->children));
2872   PetscCall(DMDestroy(&mesh->referenceTree));
2873   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2874   PetscCall(PetscFree(mesh->neighbors));
2875   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2876   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2877   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2878   PetscCall(PetscFree(mesh));
2879   PetscFunctionReturn(PETSC_SUCCESS);
2880 }
2881 
2882 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2883 {
2884   PetscSection           sectionGlobal, sectionLocal;
2885   PetscInt               bs = -1, mbs;
2886   PetscInt               localSize, localStart = 0;
2887   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2888   MatType                mtype;
2889   ISLocalToGlobalMapping ltog;
2890 
2891   PetscFunctionBegin;
2892   PetscCall(MatInitializePackage());
2893   mtype = dm->mattype;
2894   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2895   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2896   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2897   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2898   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2899   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2900   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2901   PetscCall(MatSetType(*J, mtype));
2902   PetscCall(MatSetFromOptions(*J));
2903   PetscCall(MatGetBlockSize(*J, &mbs));
2904   if (mbs > 1) bs = mbs;
2905   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2906   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2907   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2908   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2909   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2910   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2911   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2912   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2913   if (!isShell) {
2914     // There are three states with pblocks, since block starts can have no dofs:
2915     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2916     // TRUE)    Block Start: The first entry in a block has been added
2917     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2918     PetscBT         blst;
2919     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2920     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2921     const PetscInt *perm       = NULL;
2922     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2923     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2924 
2925     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2926     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2927     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2928 
2929     PetscCall(PetscCalloc1(localSize, &pblocks));
2930     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2931     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2932     // We need to process in the permuted order to get block sizes right
2933     for (PetscInt point = pStart; point < pEnd; ++point) {
2934       const PetscInt p = perm ? perm[point] : point;
2935 
2936       switch (dm->blocking_type) {
2937       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2938         PetscInt bdof, offset;
2939 
2940         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2941         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2942         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2943         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2944         if (dof > 0) {
2945           // State change
2946           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2947           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2948 
2949           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2950           // Signal block concatenation
2951           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2952         }
2953         dof  = dof < 0 ? -(dof + 1) : dof;
2954         bdof = cdof && (dof - cdof) ? 1 : dof;
2955         if (dof) {
2956           if (bs < 0) {
2957             bs = bdof;
2958           } else if (bs != bdof) {
2959             bs = 1;
2960           }
2961         }
2962       } break;
2963       case DM_BLOCKING_FIELD_NODE: {
2964         for (PetscInt field = 0; field < num_fields; field++) {
2965           PetscInt num_comp, bdof, offset;
2966           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2967           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2968           if (dof < 0) continue;
2969           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2970           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2971           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2972           PetscInt num_nodes = dof / num_comp;
2973           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2974           // Handle possibly constant block size (unlikely)
2975           bdof = cdof && (dof - cdof) ? 1 : dof;
2976           if (dof) {
2977             if (bs < 0) {
2978               bs = bdof;
2979             } else if (bs != bdof) {
2980               bs = 1;
2981             }
2982           }
2983         }
2984       } break;
2985       }
2986     }
2987     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2988     /* Must have same blocksize on all procs (some might have no points) */
2989     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2990     bsLocal[1] = bs;
2991     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2992     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2993     else bs = bsMinMax[0];
2994     bs = PetscMax(1, bs);
2995     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2996     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2997       PetscCall(MatSetBlockSize(*J, bs));
2998       PetscCall(MatSetUp(*J));
2999     } else {
3000       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3001       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3002       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3003     }
3004     if (pblocks) { // Consolidate blocks
3005       PetscInt nblocks = 0;
3006       pblocks[0]       = PetscAbs(pblocks[0]);
3007       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3008         if (pblocks[i] == 0) continue;
3009         // Negative block size indicates the blocks should be concatenated
3010         if (pblocks[i] < 0) {
3011           pblocks[i] = -pblocks[i];
3012           pblocks[nblocks - 1] += pblocks[i];
3013         } else {
3014           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3015         }
3016         for (PetscInt j = 1; j < pblocks[i]; j++)
3017           PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " at %" PetscInt_FMT " mismatches entry %" PetscInt_FMT " at %" PetscInt_FMT, pblocks[i], i, pblocks[i + j], i + j);
3018       }
3019       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3020     }
3021     PetscCall(PetscFree(pblocks));
3022   }
3023   PetscCall(MatSetDM(*J, dm));
3024   PetscFunctionReturn(PETSC_SUCCESS);
3025 }
3026 
3027 /*@
3028   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3029 
3030   Not Collective
3031 
3032   Input Parameter:
3033 . dm - The `DMPLEX`
3034 
3035   Output Parameter:
3036 . subsection - The subdomain section
3037 
3038   Level: developer
3039 
3040 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3041 @*/
3042 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3043 {
3044   DM_Plex *mesh = (DM_Plex *)dm->data;
3045 
3046   PetscFunctionBegin;
3047   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3048   if (!mesh->subdomainSection) {
3049     PetscSection section;
3050     PetscSF      sf;
3051 
3052     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3053     PetscCall(DMGetLocalSection(dm, &section));
3054     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3055     PetscCall(PetscSFDestroy(&sf));
3056   }
3057   *subsection = mesh->subdomainSection;
3058   PetscFunctionReturn(PETSC_SUCCESS);
3059 }
3060 
3061 /*@
3062   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3063 
3064   Not Collective
3065 
3066   Input Parameter:
3067 . dm - The `DMPLEX`
3068 
3069   Output Parameters:
3070 + pStart - The first mesh point
3071 - pEnd   - The upper bound for mesh points
3072 
3073   Level: beginner
3074 
3075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3076 @*/
3077 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3078 {
3079   DM_Plex *mesh = (DM_Plex *)dm->data;
3080 
3081   PetscFunctionBegin;
3082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3083   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3084   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3085   PetscFunctionReturn(PETSC_SUCCESS);
3086 }
3087 
3088 /*@
3089   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3090 
3091   Not Collective
3092 
3093   Input Parameters:
3094 + dm     - The `DMPLEX`
3095 . pStart - The first mesh point
3096 - pEnd   - The upper bound for mesh points
3097 
3098   Level: beginner
3099 
3100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3101 @*/
3102 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3103 {
3104   DM_Plex *mesh = (DM_Plex *)dm->data;
3105 
3106   PetscFunctionBegin;
3107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3108   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3109   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3110   PetscCall(PetscFree(mesh->cellTypes));
3111   PetscFunctionReturn(PETSC_SUCCESS);
3112 }
3113 
3114 /*@
3115   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3116 
3117   Not Collective
3118 
3119   Input Parameters:
3120 + dm - The `DMPLEX`
3121 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3122 
3123   Output Parameter:
3124 . size - The cone size for point `p`
3125 
3126   Level: beginner
3127 
3128 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3129 @*/
3130 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3131 {
3132   DM_Plex *mesh = (DM_Plex *)dm->data;
3133 
3134   PetscFunctionBegin;
3135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3136   PetscAssertPointer(size, 3);
3137   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3138   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3139   PetscFunctionReturn(PETSC_SUCCESS);
3140 }
3141 
3142 /*@
3143   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3144 
3145   Not Collective
3146 
3147   Input Parameters:
3148 + dm   - The `DMPLEX`
3149 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3150 - size - The cone size for point `p`
3151 
3152   Level: beginner
3153 
3154   Note:
3155   This should be called after `DMPlexSetChart()`.
3156 
3157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3158 @*/
3159 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3160 {
3161   DM_Plex *mesh = (DM_Plex *)dm->data;
3162 
3163   PetscFunctionBegin;
3164   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3165   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3166   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3167   PetscFunctionReturn(PETSC_SUCCESS);
3168 }
3169 
3170 /*@C
3171   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3172 
3173   Not Collective
3174 
3175   Input Parameters:
3176 + dm - The `DMPLEX`
3177 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3178 
3179   Output Parameter:
3180 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3181 
3182   Level: beginner
3183 
3184   Fortran Notes:
3185   `cone` must be declared with
3186 .vb
3187   PetscInt, pointer :: cone(:)
3188 .ve
3189 
3190   You must call `DMPlexRestoreCone()` after you finish using the array.
3191   `DMPlexRestoreCone()` is not needed/available in C.
3192 
3193 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3194 @*/
3195 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3196 {
3197   DM_Plex *mesh = (DM_Plex *)dm->data;
3198   PetscInt off;
3199 
3200   PetscFunctionBegin;
3201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3202   PetscAssertPointer(cone, 3);
3203   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3204   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3205   PetscFunctionReturn(PETSC_SUCCESS);
3206 }
3207 
3208 /*@
3209   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3210 
3211   Not Collective
3212 
3213   Input Parameters:
3214 + dm - The `DMPLEX`
3215 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3216 
3217   Output Parameters:
3218 + pConesSection - `PetscSection` describing the layout of `pCones`
3219 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3220 
3221   Level: intermediate
3222 
3223 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3224 @*/
3225 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3226 {
3227   PetscSection cs, newcs;
3228   PetscInt    *cones;
3229   PetscInt    *newarr = NULL;
3230   PetscInt     n;
3231 
3232   PetscFunctionBegin;
3233   PetscCall(DMPlexGetCones(dm, &cones));
3234   PetscCall(DMPlexGetConeSection(dm, &cs));
3235   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3236   if (pConesSection) *pConesSection = newcs;
3237   if (pCones) {
3238     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3239     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3240   }
3241   PetscFunctionReturn(PETSC_SUCCESS);
3242 }
3243 
3244 /*@
3245   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3246 
3247   Not Collective
3248 
3249   Input Parameters:
3250 + dm     - The `DMPLEX`
3251 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3252 
3253   Output Parameter:
3254 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3255 
3256   Level: advanced
3257 
3258   Notes:
3259   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3260 
3261   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3262 
3263 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3264           `DMPlexGetDepth()`, `IS`
3265 @*/
3266 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3267 {
3268   IS      *expandedPointsAll;
3269   PetscInt depth;
3270 
3271   PetscFunctionBegin;
3272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3273   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3274   PetscAssertPointer(expandedPoints, 3);
3275   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3276   *expandedPoints = expandedPointsAll[0];
3277   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3278   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3279   PetscFunctionReturn(PETSC_SUCCESS);
3280 }
3281 
3282 /*@
3283   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3284   (DAG points of depth 0, i.e., without cones).
3285 
3286   Not Collective
3287 
3288   Input Parameters:
3289 + dm     - The `DMPLEX`
3290 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3291 
3292   Output Parameters:
3293 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3294 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3295 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3296 
3297   Level: advanced
3298 
3299   Notes:
3300   Like `DMPlexGetConeTuple()` but recursive.
3301 
3302   Array `expandedPoints` has size equal to `depth`. Each `expandedPoints`[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
3303   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3304 
3305   Array section has size equal to `depth`.  Each `PetscSection` `sections`[d] realizes mapping from `expandedPoints`[d+1] (section points) to `expandedPoints`[d] (section dofs) as follows\:
3306   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3307   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3308 
3309 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3310           `DMPlexGetDepth()`, `PetscSection`, `IS`
3311 @*/
3312 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3313 {
3314   const PetscInt *arr0 = NULL, *cone = NULL;
3315   PetscInt       *arr = NULL, *newarr = NULL;
3316   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3317   IS             *expandedPoints_;
3318   PetscSection   *sections_;
3319 
3320   PetscFunctionBegin;
3321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3322   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3323   if (depth) PetscAssertPointer(depth, 3);
3324   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3325   if (sections) PetscAssertPointer(sections, 5);
3326   PetscCall(ISGetLocalSize(points, &n));
3327   PetscCall(ISGetIndices(points, &arr0));
3328   PetscCall(DMPlexGetDepth(dm, &depth_));
3329   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3330   PetscCall(PetscCalloc1(depth_, &sections_));
3331   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3332   for (d = depth_ - 1; d >= 0; d--) {
3333     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3334     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3335     for (i = 0; i < n; i++) {
3336       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3337       if (arr[i] >= start && arr[i] < end) {
3338         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3339         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3340       } else {
3341         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3342       }
3343     }
3344     PetscCall(PetscSectionSetUp(sections_[d]));
3345     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3346     PetscCall(PetscMalloc1(newn, &newarr));
3347     for (i = 0; i < n; i++) {
3348       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3349       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3350       if (cn > 1) {
3351         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3352         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3353       } else {
3354         newarr[co] = arr[i];
3355       }
3356     }
3357     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3358     arr = newarr;
3359     n   = newn;
3360   }
3361   PetscCall(ISRestoreIndices(points, &arr0));
3362   *depth = depth_;
3363   if (expandedPoints) *expandedPoints = expandedPoints_;
3364   else {
3365     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3366     PetscCall(PetscFree(expandedPoints_));
3367   }
3368   if (sections) *sections = sections_;
3369   else {
3370     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3371     PetscCall(PetscFree(sections_));
3372   }
3373   PetscFunctionReturn(PETSC_SUCCESS);
3374 }
3375 
3376 /*@
3377   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3378 
3379   Not Collective
3380 
3381   Input Parameters:
3382 + dm     - The `DMPLEX`
3383 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3384 
3385   Output Parameters:
3386 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3387 . expandedPoints - (optional) An array of recursively expanded cones
3388 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3389 
3390   Level: advanced
3391 
3392   Note:
3393   See `DMPlexGetConeRecursive()`
3394 
3395 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3396           `DMPlexGetDepth()`, `IS`, `PetscSection`
3397 @*/
3398 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3399 {
3400   PetscInt d, depth_;
3401 
3402   PetscFunctionBegin;
3403   PetscCall(DMPlexGetDepth(dm, &depth_));
3404   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3405   if (depth) *depth = 0;
3406   if (expandedPoints) {
3407     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3408     PetscCall(PetscFree(*expandedPoints));
3409   }
3410   if (sections) {
3411     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3412     PetscCall(PetscFree(*sections));
3413   }
3414   PetscFunctionReturn(PETSC_SUCCESS);
3415 }
3416 
3417 /*@
3418   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3419 
3420   Not Collective
3421 
3422   Input Parameters:
3423 + dm   - The `DMPLEX`
3424 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3425 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3426 
3427   Level: beginner
3428 
3429   Note:
3430   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3431 
3432 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3433 @*/
3434 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3435 {
3436   DM_Plex *mesh = (DM_Plex *)dm->data;
3437   PetscInt dof, off, c;
3438 
3439   PetscFunctionBegin;
3440   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3441   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3442   if (dof) PetscAssertPointer(cone, 3);
3443   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3444   if (PetscDefined(USE_DEBUG)) {
3445     PetscInt pStart, pEnd;
3446     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3447     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3448     for (c = 0; c < dof; ++c) {
3449       PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3450       mesh->cones[off + c] = cone[c];
3451     }
3452   } else {
3453     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3454   }
3455   PetscFunctionReturn(PETSC_SUCCESS);
3456 }
3457 
3458 /*@C
3459   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3460 
3461   Not Collective
3462 
3463   Input Parameters:
3464 + dm - The `DMPLEX`
3465 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3466 
3467   Output Parameter:
3468 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3469                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3470 
3471   Level: beginner
3472 
3473   Note:
3474   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3475   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3476   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3477   with the identity.
3478 
3479   Fortran Notes:
3480   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3481   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3482 
3483 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3484           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3485 @*/
3486 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3487 {
3488   DM_Plex *mesh = (DM_Plex *)dm->data;
3489   PetscInt off;
3490 
3491   PetscFunctionBegin;
3492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3493   if (PetscDefined(USE_DEBUG)) {
3494     PetscInt dof;
3495     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3496     if (dof) PetscAssertPointer(coneOrientation, 3);
3497   }
3498   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3499 
3500   *coneOrientation = &mesh->coneOrientations[off];
3501   PetscFunctionReturn(PETSC_SUCCESS);
3502 }
3503 
3504 /*@
3505   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3506 
3507   Not Collective
3508 
3509   Input Parameters:
3510 + dm              - The `DMPLEX`
3511 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3512 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3513 
3514   Level: beginner
3515 
3516   Notes:
3517   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3518 
3519   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3520 
3521 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3522 @*/
3523 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3524 {
3525   DM_Plex *mesh = (DM_Plex *)dm->data;
3526   PetscInt pStart, pEnd;
3527   PetscInt dof, off, c;
3528 
3529   PetscFunctionBegin;
3530   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3531   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3532   if (dof) PetscAssertPointer(coneOrientation, 3);
3533   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3534   if (PetscDefined(USE_DEBUG)) {
3535     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3536     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3537     for (c = 0; c < dof; ++c) {
3538       PetscInt cdof, o = coneOrientation[c];
3539 
3540       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3541       PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3542       mesh->coneOrientations[off + c] = o;
3543     }
3544   } else {
3545     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3546   }
3547   PetscFunctionReturn(PETSC_SUCCESS);
3548 }
3549 
3550 /*@
3551   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3552 
3553   Not Collective
3554 
3555   Input Parameters:
3556 + dm        - The `DMPLEX`
3557 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3558 . conePos   - The local index in the cone where the point should be put
3559 - conePoint - The mesh point to insert
3560 
3561   Level: beginner
3562 
3563 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3564 @*/
3565 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3566 {
3567   DM_Plex *mesh = (DM_Plex *)dm->data;
3568   PetscInt pStart, pEnd;
3569   PetscInt dof, off;
3570 
3571   PetscFunctionBegin;
3572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3573   if (PetscDefined(USE_DEBUG)) {
3574     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3575     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3576     PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3577     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3578     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3579   }
3580   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3581   mesh->cones[off + conePos] = conePoint;
3582   PetscFunctionReturn(PETSC_SUCCESS);
3583 }
3584 
3585 /*@
3586   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3587 
3588   Not Collective
3589 
3590   Input Parameters:
3591 + dm              - The `DMPLEX`
3592 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3593 . conePos         - The local index in the cone where the point should be put
3594 - coneOrientation - The point orientation to insert
3595 
3596   Level: beginner
3597 
3598   Note:
3599   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3600 
3601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3602 @*/
3603 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3604 {
3605   DM_Plex *mesh = (DM_Plex *)dm->data;
3606   PetscInt pStart, pEnd;
3607   PetscInt dof, off;
3608 
3609   PetscFunctionBegin;
3610   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3611   if (PetscDefined(USE_DEBUG)) {
3612     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3613     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3614     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3615     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3616   }
3617   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3618   mesh->coneOrientations[off + conePos] = coneOrientation;
3619   PetscFunctionReturn(PETSC_SUCCESS);
3620 }
3621 
3622 /*@C
3623   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3624 
3625   Not collective
3626 
3627   Input Parameters:
3628 + dm - The DMPlex
3629 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3630 
3631   Output Parameters:
3632 + cone - An array of points which are on the in-edges for point `p`
3633 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3634          integer giving the prescription for cone traversal.
3635 
3636   Level: beginner
3637 
3638   Notes:
3639   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3640   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3641   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3642   with the identity.
3643 
3644   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3645 
3646   Fortran Notes:
3647   `cone` and `ornt` must be declared with
3648 .vb
3649   PetscInt, pointer :: cone(:)
3650   PetscInt, pointer :: ornt(:)
3651 .ve
3652 
3653 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3654 @*/
3655 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3656 {
3657   DM_Plex *mesh = (DM_Plex *)dm->data;
3658 
3659   PetscFunctionBegin;
3660   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3661   if (mesh->tr) {
3662     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3663   } else {
3664     PetscInt off;
3665     if (PetscDefined(USE_DEBUG)) {
3666       PetscInt dof;
3667       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3668       if (dof) {
3669         if (cone) PetscAssertPointer(cone, 3);
3670         if (ornt) PetscAssertPointer(ornt, 4);
3671       }
3672     }
3673     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3674     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3675     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3676   }
3677   PetscFunctionReturn(PETSC_SUCCESS);
3678 }
3679 
3680 /*@C
3681   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3682 
3683   Not Collective
3684 
3685   Input Parameters:
3686 + dm   - The DMPlex
3687 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3688 . cone - An array of points which are on the in-edges for point p
3689 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3690          integer giving the prescription for cone traversal.
3691 
3692   Level: beginner
3693 
3694 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3695 @*/
3696 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3697 {
3698   DM_Plex *mesh = (DM_Plex *)dm->data;
3699 
3700   PetscFunctionBegin;
3701   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3702   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3703   PetscFunctionReturn(PETSC_SUCCESS);
3704 }
3705 
3706 /*@
3707   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3708 
3709   Not Collective
3710 
3711   Input Parameters:
3712 + dm - The `DMPLEX`
3713 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3714 
3715   Output Parameter:
3716 . size - The support size for point `p`
3717 
3718   Level: beginner
3719 
3720 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3721 @*/
3722 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3723 {
3724   DM_Plex *mesh = (DM_Plex *)dm->data;
3725 
3726   PetscFunctionBegin;
3727   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3728   PetscAssertPointer(size, 3);
3729   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3730   PetscFunctionReturn(PETSC_SUCCESS);
3731 }
3732 
3733 /*@
3734   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3735 
3736   Not Collective
3737 
3738   Input Parameters:
3739 + dm   - The `DMPLEX`
3740 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3741 - size - The support size for point `p`
3742 
3743   Level: beginner
3744 
3745   Note:
3746   This should be called after `DMPlexSetChart()`.
3747 
3748 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3749 @*/
3750 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3751 {
3752   DM_Plex *mesh = (DM_Plex *)dm->data;
3753 
3754   PetscFunctionBegin;
3755   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3756   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3757   PetscFunctionReturn(PETSC_SUCCESS);
3758 }
3759 
3760 /*@C
3761   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3762 
3763   Not Collective
3764 
3765   Input Parameters:
3766 + dm - The `DMPLEX`
3767 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3768 
3769   Output Parameter:
3770 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3771 
3772   Level: beginner
3773 
3774   Fortran Notes:
3775   `support` must be declared with
3776 .vb
3777   PetscInt, pointer :: support(:)
3778 .ve
3779 
3780   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3781   `DMPlexRestoreSupport()` is not needed/available in C.
3782 
3783 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3784 @*/
3785 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3786 {
3787   DM_Plex *mesh = (DM_Plex *)dm->data;
3788   PetscInt off;
3789 
3790   PetscFunctionBegin;
3791   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3792   PetscAssertPointer(support, 3);
3793   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3794   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3795   PetscFunctionReturn(PETSC_SUCCESS);
3796 }
3797 
3798 /*@
3799   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3800 
3801   Not Collective
3802 
3803   Input Parameters:
3804 + dm      - The `DMPLEX`
3805 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3806 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3807 
3808   Level: beginner
3809 
3810   Note:
3811   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3812 
3813 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3814 @*/
3815 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3816 {
3817   DM_Plex *mesh = (DM_Plex *)dm->data;
3818   PetscInt pStart, pEnd;
3819   PetscInt dof, off, c;
3820 
3821   PetscFunctionBegin;
3822   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3823   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3824   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3825   if (dof) PetscAssertPointer(support, 3);
3826   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3827   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3828   for (c = 0; c < dof; ++c) {
3829     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3830     mesh->supports[off + c] = support[c];
3831   }
3832   PetscFunctionReturn(PETSC_SUCCESS);
3833 }
3834 
3835 /*@
3836   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3837 
3838   Not Collective
3839 
3840   Input Parameters:
3841 + dm           - The `DMPLEX`
3842 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3843 . supportPos   - The local index in the cone where the point should be put
3844 - supportPoint - The mesh point to insert
3845 
3846   Level: beginner
3847 
3848 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3849 @*/
3850 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3851 {
3852   DM_Plex *mesh = (DM_Plex *)dm->data;
3853   PetscInt pStart, pEnd;
3854   PetscInt dof, off;
3855 
3856   PetscFunctionBegin;
3857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3858   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3859   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3860   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3861   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3862   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3863   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3864   mesh->supports[off + supportPos] = supportPoint;
3865   PetscFunctionReturn(PETSC_SUCCESS);
3866 }
3867 
3868 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3869 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3870 {
3871   switch (ct) {
3872   case DM_POLYTOPE_SEGMENT:
3873     if (o == -1) return -2;
3874     break;
3875   case DM_POLYTOPE_TRIANGLE:
3876     if (o == -3) return -1;
3877     if (o == -2) return -3;
3878     if (o == -1) return -2;
3879     break;
3880   case DM_POLYTOPE_QUADRILATERAL:
3881     if (o == -4) return -2;
3882     if (o == -3) return -1;
3883     if (o == -2) return -4;
3884     if (o == -1) return -3;
3885     break;
3886   default:
3887     return o;
3888   }
3889   return o;
3890 }
3891 
3892 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3893 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3894 {
3895   switch (ct) {
3896   case DM_POLYTOPE_SEGMENT:
3897     if ((o == -2) || (o == 1)) return -1;
3898     if (o == -1) return 0;
3899     break;
3900   case DM_POLYTOPE_TRIANGLE:
3901     if (o == -3) return -2;
3902     if (o == -2) return -1;
3903     if (o == -1) return -3;
3904     break;
3905   case DM_POLYTOPE_QUADRILATERAL:
3906     if (o == -4) return -2;
3907     if (o == -3) return -1;
3908     if (o == -2) return -4;
3909     if (o == -1) return -3;
3910     break;
3911   default:
3912     return o;
3913   }
3914   return o;
3915 }
3916 
3917 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3918 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3919 {
3920   PetscInt pStart, pEnd, p;
3921 
3922   PetscFunctionBegin;
3923   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3924   for (p = pStart; p < pEnd; ++p) {
3925     const PetscInt *cone, *ornt;
3926     PetscInt        coneSize, c;
3927 
3928     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3929     PetscCall(DMPlexGetCone(dm, p, &cone));
3930     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3931     for (c = 0; c < coneSize; ++c) {
3932       DMPolytopeType ct;
3933       const PetscInt o = ornt[c];
3934 
3935       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3936       switch (ct) {
3937       case DM_POLYTOPE_SEGMENT:
3938         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3939         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3940         break;
3941       case DM_POLYTOPE_TRIANGLE:
3942         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3943         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3944         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3945         break;
3946       case DM_POLYTOPE_QUADRILATERAL:
3947         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3948         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3949         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3950         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3951         break;
3952       default:
3953         break;
3954       }
3955     }
3956   }
3957   PetscFunctionReturn(PETSC_SUCCESS);
3958 }
3959 
3960 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3961 {
3962   DM_Plex *mesh = (DM_Plex *)dm->data;
3963 
3964   PetscFunctionBeginHot;
3965   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3966     if (useCone) {
3967       PetscCall(DMPlexGetConeSize(dm, p, size));
3968       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3969     } else {
3970       PetscCall(DMPlexGetSupportSize(dm, p, size));
3971       PetscCall(DMPlexGetSupport(dm, p, arr));
3972     }
3973   } else {
3974     if (useCone) {
3975       const PetscSection s   = mesh->coneSection;
3976       const PetscInt     ps  = p - s->pStart;
3977       const PetscInt     off = s->atlasOff[ps];
3978 
3979       *size = s->atlasDof[ps];
3980       *arr  = mesh->cones + off;
3981       *ornt = mesh->coneOrientations + off;
3982     } else {
3983       const PetscSection s   = mesh->supportSection;
3984       const PetscInt     ps  = p - s->pStart;
3985       const PetscInt     off = s->atlasOff[ps];
3986 
3987       *size = s->atlasDof[ps];
3988       *arr  = mesh->supports + off;
3989     }
3990   }
3991   PetscFunctionReturn(PETSC_SUCCESS);
3992 }
3993 
3994 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3995 {
3996   DM_Plex *mesh = (DM_Plex *)dm->data;
3997 
3998   PetscFunctionBeginHot;
3999   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4000     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4001   }
4002   PetscFunctionReturn(PETSC_SUCCESS);
4003 }
4004 
4005 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4006 {
4007   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4008   PetscInt       *closure;
4009   const PetscInt *tmp = NULL, *tmpO = NULL;
4010   PetscInt        off = 0, tmpSize, t;
4011 
4012   PetscFunctionBeginHot;
4013   if (ornt) {
4014     PetscCall(DMPlexGetCellType(dm, p, &ct));
4015     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
4016   }
4017   if (*points) {
4018     closure = *points;
4019   } else {
4020     PetscInt maxConeSize, maxSupportSize;
4021     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4022     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4023   }
4024   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4025   if (ct == DM_POLYTOPE_UNKNOWN) {
4026     closure[off++] = p;
4027     closure[off++] = 0;
4028     for (t = 0; t < tmpSize; ++t) {
4029       closure[off++] = tmp[t];
4030       closure[off++] = tmpO ? tmpO[t] : 0;
4031     }
4032   } else {
4033     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4034 
4035     /* We assume that cells with a valid type have faces with a valid type */
4036     closure[off++] = p;
4037     closure[off++] = ornt;
4038     for (t = 0; t < tmpSize; ++t) {
4039       DMPolytopeType ft;
4040 
4041       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4042       closure[off++] = tmp[arr[t]];
4043       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4044     }
4045   }
4046   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4047   if (numPoints) *numPoints = tmpSize + 1;
4048   if (points) *points = closure;
4049   PetscFunctionReturn(PETSC_SUCCESS);
4050 }
4051 
4052 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4053 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4054 {
4055   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4056   const PetscInt *cone, *ornt;
4057   PetscInt       *pts, *closure = NULL;
4058   DMPolytopeType  ft;
4059   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4060   PetscInt        dim, coneSize, c, d, clSize, cl;
4061 
4062   PetscFunctionBeginHot;
4063   PetscCall(DMGetDimension(dm, &dim));
4064   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4065   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4066   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4067   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4068   maxSize       = PetscMax(coneSeries, supportSeries);
4069   if (*points) {
4070     pts = *points;
4071   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4072   c        = 0;
4073   pts[c++] = point;
4074   pts[c++] = o;
4075   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4076   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4077   for (cl = 0; cl < clSize * 2; cl += 2) {
4078     pts[c++] = closure[cl];
4079     pts[c++] = closure[cl + 1];
4080   }
4081   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4082   for (cl = 0; cl < clSize * 2; cl += 2) {
4083     pts[c++] = closure[cl];
4084     pts[c++] = closure[cl + 1];
4085   }
4086   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4087   for (d = 2; d < coneSize; ++d) {
4088     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4089     pts[c++] = cone[arr[d * 2 + 0]];
4090     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4091   }
4092   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4093   if (dim >= 3) {
4094     for (d = 2; d < coneSize; ++d) {
4095       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4096       const PetscInt *fcone, *fornt;
4097       PetscInt        fconeSize, fc, i;
4098 
4099       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4100       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4101       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4102       for (fc = 0; fc < fconeSize; ++fc) {
4103         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4104         const PetscInt co = farr[fc * 2 + 1];
4105 
4106         for (i = 0; i < c; i += 2)
4107           if (pts[i] == cp) break;
4108         if (i == c) {
4109           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4110           pts[c++] = cp;
4111           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4112         }
4113       }
4114       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4115     }
4116   }
4117   *numPoints = c / 2;
4118   *points    = pts;
4119   PetscFunctionReturn(PETSC_SUCCESS);
4120 }
4121 
4122 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4123 {
4124   DMPolytopeType ct;
4125   PetscInt      *closure, *fifo;
4126   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4127   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4128   PetscInt       depth, maxSize;
4129 
4130   PetscFunctionBeginHot;
4131   PetscCall(DMPlexGetDepth(dm, &depth));
4132   if (depth == 1) {
4133     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4134     PetscFunctionReturn(PETSC_SUCCESS);
4135   }
4136   PetscCall(DMPlexGetCellType(dm, p, &ct));
4137   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
4138   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4139     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4140     PetscFunctionReturn(PETSC_SUCCESS);
4141   }
4142   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4143   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4144   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4145   maxSize       = PetscMax(coneSeries, supportSeries);
4146   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4147   if (*points) {
4148     closure = *points;
4149   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4150   closure[closureSize++] = p;
4151   closure[closureSize++] = ornt;
4152   fifo[fifoSize++]       = p;
4153   fifo[fifoSize++]       = ornt;
4154   fifo[fifoSize++]       = ct;
4155   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4156   while (fifoSize - fifoStart) {
4157     const PetscInt       q    = fifo[fifoStart++];
4158     const PetscInt       o    = fifo[fifoStart++];
4159     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4160     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4161     const PetscInt      *tmp, *tmpO = NULL;
4162     PetscInt             tmpSize, t;
4163 
4164     if (PetscDefined(USE_DEBUG)) {
4165       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4166       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
4167     }
4168     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4169     for (t = 0; t < tmpSize; ++t) {
4170       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4171       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4172       const PetscInt cp = tmp[ip];
4173       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4174       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4175       PetscInt       c;
4176 
4177       /* Check for duplicate */
4178       for (c = 0; c < closureSize; c += 2) {
4179         if (closure[c] == cp) break;
4180       }
4181       if (c == closureSize) {
4182         closure[closureSize++] = cp;
4183         closure[closureSize++] = co;
4184         fifo[fifoSize++]       = cp;
4185         fifo[fifoSize++]       = co;
4186         fifo[fifoSize++]       = ct;
4187       }
4188     }
4189     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4190   }
4191   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4192   if (numPoints) *numPoints = closureSize / 2;
4193   if (points) *points = closure;
4194   PetscFunctionReturn(PETSC_SUCCESS);
4195 }
4196 
4197 /*@C
4198   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4199 
4200   Not Collective
4201 
4202   Input Parameters:
4203 + dm      - The `DMPLEX`
4204 . p       - The mesh point
4205 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4206 
4207   Input/Output Parameter:
4208 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4209            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4210            otherwise the provided array is used to hold the values
4211 
4212   Output Parameter:
4213 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4214 
4215   Level: beginner
4216 
4217   Note:
4218   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4219 
4220   Fortran Notes:
4221   `points` must be declared with
4222 .vb
4223   PetscInt, pointer :: points(:)
4224 .ve
4225   and is always allocated by the function.
4226 
4227   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4228 
4229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4230 @*/
4231 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4232 {
4233   PetscFunctionBeginHot;
4234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4235   if (numPoints) PetscAssertPointer(numPoints, 4);
4236   if (points) PetscAssertPointer(points, 5);
4237   if (PetscDefined(USE_DEBUG)) {
4238     PetscInt pStart, pEnd;
4239     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4240     PetscCheck(p >= pStart && p < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " is not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
4241   }
4242   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4243   PetscFunctionReturn(PETSC_SUCCESS);
4244 }
4245 
4246 /*@C
4247   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4248 
4249   Not Collective
4250 
4251   Input Parameters:
4252 + dm        - The `DMPLEX`
4253 . p         - The mesh point
4254 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4255 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4256 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4257 
4258   Level: beginner
4259 
4260   Note:
4261   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4262 
4263 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4264 @*/
4265 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4266 {
4267   PetscFunctionBeginHot;
4268   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4269   if (numPoints) *numPoints = 0;
4270   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4271   PetscFunctionReturn(PETSC_SUCCESS);
4272 }
4273 
4274 /*@
4275   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4276 
4277   Not Collective
4278 
4279   Input Parameter:
4280 . dm - The `DMPLEX`
4281 
4282   Output Parameters:
4283 + maxConeSize    - The maximum number of in-edges
4284 - maxSupportSize - The maximum number of out-edges
4285 
4286   Level: beginner
4287 
4288 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4289 @*/
4290 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4291 {
4292   DM_Plex *mesh = (DM_Plex *)dm->data;
4293 
4294   PetscFunctionBegin;
4295   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4296   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4297   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4298   PetscFunctionReturn(PETSC_SUCCESS);
4299 }
4300 
4301 PetscErrorCode DMSetUp_Plex(DM dm)
4302 {
4303   DM_Plex *mesh = (DM_Plex *)dm->data;
4304   PetscInt size, maxSupportSize;
4305 
4306   PetscFunctionBegin;
4307   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4308   PetscCall(PetscSectionSetUp(mesh->coneSection));
4309   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4310   PetscCall(PetscMalloc1(size, &mesh->cones));
4311   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4312   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4313   if (maxSupportSize) {
4314     PetscCall(PetscSectionSetUp(mesh->supportSection));
4315     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4316     PetscCall(PetscMalloc1(size, &mesh->supports));
4317   }
4318   PetscFunctionReturn(PETSC_SUCCESS);
4319 }
4320 
4321 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4322 {
4323   PetscFunctionBegin;
4324   if (subdm) PetscCall(DMClone(dm, subdm));
4325   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4326   if (subdm) (*subdm)->useNatural = dm->useNatural;
4327   if (dm->useNatural && dm->sfMigration) {
4328     PetscSF sfNatural;
4329 
4330     (*subdm)->sfMigration = dm->sfMigration;
4331     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4332     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4333     (*subdm)->sfNatural = sfNatural;
4334   }
4335   PetscFunctionReturn(PETSC_SUCCESS);
4336 }
4337 
4338 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4339 {
4340   PetscInt i = 0;
4341 
4342   PetscFunctionBegin;
4343   PetscCall(DMClone(dms[0], superdm));
4344   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4345   (*superdm)->useNatural = PETSC_FALSE;
4346   for (i = 0; i < len; i++) {
4347     if (dms[i]->useNatural && dms[i]->sfMigration) {
4348       PetscSF sfNatural;
4349 
4350       (*superdm)->sfMigration = dms[i]->sfMigration;
4351       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4352       (*superdm)->useNatural = PETSC_TRUE;
4353       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4354       (*superdm)->sfNatural = sfNatural;
4355       break;
4356     }
4357   }
4358   PetscFunctionReturn(PETSC_SUCCESS);
4359 }
4360 
4361 /*@
4362   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4363 
4364   Not Collective
4365 
4366   Input Parameter:
4367 . dm - The `DMPLEX`
4368 
4369   Level: beginner
4370 
4371   Note:
4372   This should be called after all calls to `DMPlexSetCone()`
4373 
4374 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4375 @*/
4376 PetscErrorCode DMPlexSymmetrize(DM dm)
4377 {
4378   DM_Plex  *mesh = (DM_Plex *)dm->data;
4379   PetscInt *offsets;
4380   PetscInt  supportSize;
4381   PetscInt  pStart, pEnd, p;
4382 
4383   PetscFunctionBegin;
4384   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4385   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4386   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4387   /* Calculate support sizes */
4388   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4389   for (p = pStart; p < pEnd; ++p) {
4390     PetscInt dof, off, c;
4391 
4392     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4393     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4394     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4395   }
4396   PetscCall(PetscSectionSetUp(mesh->supportSection));
4397   /* Calculate supports */
4398   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4399   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4400   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4401   for (p = pStart; p < pEnd; ++p) {
4402     PetscInt dof, off, c;
4403 
4404     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4405     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4406     for (c = off; c < off + dof; ++c) {
4407       const PetscInt q = mesh->cones[c];
4408       PetscInt       offS;
4409 
4410       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4411 
4412       mesh->supports[offS + offsets[q]] = p;
4413       ++offsets[q];
4414     }
4415   }
4416   PetscCall(PetscFree(offsets));
4417   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4418   PetscFunctionReturn(PETSC_SUCCESS);
4419 }
4420 
4421 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4422 {
4423   IS stratumIS;
4424 
4425   PetscFunctionBegin;
4426   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4427   if (PetscDefined(USE_DEBUG)) {
4428     PetscInt  qStart, qEnd, numLevels, level;
4429     PetscBool overlap = PETSC_FALSE;
4430     PetscCall(DMLabelGetNumValues(label, &numLevels));
4431     for (level = 0; level < numLevels; level++) {
4432       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4433       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4434         overlap = PETSC_TRUE;
4435         break;
4436       }
4437     }
4438     PetscCheck(!overlap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
4439   }
4440   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4441   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4442   PetscCall(ISDestroy(&stratumIS));
4443   PetscFunctionReturn(PETSC_SUCCESS);
4444 }
4445 
4446 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4447 {
4448   PetscInt *pMin, *pMax;
4449   PetscInt  pStart, pEnd;
4450   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4451 
4452   PetscFunctionBegin;
4453   {
4454     DMLabel label2;
4455 
4456     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4457     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4458   }
4459   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4460   for (PetscInt p = pStart; p < pEnd; ++p) {
4461     DMPolytopeType ct;
4462 
4463     PetscCall(DMPlexGetCellType(dm, p, &ct));
4464     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4465     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4466   }
4467   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4468   for (PetscInt d = dmin; d <= dmax; ++d) {
4469     pMin[d] = PETSC_INT_MAX;
4470     pMax[d] = PETSC_INT_MIN;
4471   }
4472   for (PetscInt p = pStart; p < pEnd; ++p) {
4473     DMPolytopeType ct;
4474     PetscInt       d;
4475 
4476     PetscCall(DMPlexGetCellType(dm, p, &ct));
4477     d       = DMPolytopeTypeGetDim(ct);
4478     pMin[d] = PetscMin(p, pMin[d]);
4479     pMax[d] = PetscMax(p, pMax[d]);
4480   }
4481   for (PetscInt d = dmin; d <= dmax; ++d) {
4482     if (pMin[d] > pMax[d]) continue;
4483     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4484   }
4485   PetscCall(PetscFree2(pMin, pMax));
4486   PetscFunctionReturn(PETSC_SUCCESS);
4487 }
4488 
4489 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4490 {
4491   PetscInt pStart, pEnd;
4492   PetscInt numRoots = 0, numLeaves = 0;
4493 
4494   PetscFunctionBegin;
4495   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4496   {
4497     /* Initialize roots and count leaves */
4498     PetscInt sMin = PETSC_INT_MAX;
4499     PetscInt sMax = PETSC_INT_MIN;
4500     PetscInt coneSize, supportSize;
4501 
4502     for (PetscInt p = pStart; p < pEnd; ++p) {
4503       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4504       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4505       if (!coneSize && supportSize) {
4506         sMin = PetscMin(p, sMin);
4507         sMax = PetscMax(p, sMax);
4508         ++numRoots;
4509       } else if (!supportSize && coneSize) {
4510         ++numLeaves;
4511       } else if (!supportSize && !coneSize) {
4512         /* Isolated points */
4513         sMin = PetscMin(p, sMin);
4514         sMax = PetscMax(p, sMax);
4515       }
4516     }
4517     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4518   }
4519 
4520   if (numRoots + numLeaves == (pEnd - pStart)) {
4521     PetscInt sMin = PETSC_INT_MAX;
4522     PetscInt sMax = PETSC_INT_MIN;
4523     PetscInt coneSize, supportSize;
4524 
4525     for (PetscInt p = pStart; p < pEnd; ++p) {
4526       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4527       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4528       if (!supportSize && coneSize) {
4529         sMin = PetscMin(p, sMin);
4530         sMax = PetscMax(p, sMax);
4531       }
4532     }
4533     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4534   } else {
4535     PetscInt level = 0;
4536     PetscInt qStart, qEnd;
4537 
4538     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4539     while (qEnd > qStart) {
4540       PetscInt sMin = PETSC_INT_MAX;
4541       PetscInt sMax = PETSC_INT_MIN;
4542 
4543       for (PetscInt q = qStart; q < qEnd; ++q) {
4544         const PetscInt *support;
4545         PetscInt        supportSize;
4546 
4547         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4548         PetscCall(DMPlexGetSupport(dm, q, &support));
4549         for (PetscInt s = 0; s < supportSize; ++s) {
4550           sMin = PetscMin(support[s], sMin);
4551           sMax = PetscMax(support[s], sMax);
4552         }
4553       }
4554       PetscCall(DMLabelGetNumValues(label, &level));
4555       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4556       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4557     }
4558   }
4559   PetscFunctionReturn(PETSC_SUCCESS);
4560 }
4561 
4562 /*@
4563   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4564 
4565   Collective
4566 
4567   Input Parameter:
4568 . dm - The `DMPLEX`
4569 
4570   Level: beginner
4571 
4572   Notes:
4573   The strata group all points of the same grade, and this function calculates the strata. This
4574   grade can be seen as the height (or depth) of the point in the DAG.
4575 
4576   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4577   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4578   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4579   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4580   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4581   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4582   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4583 
4584   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4585   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4586   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
4587   to interpolate only that one (e0), so that
4588 .vb
4589   cone(c0) = {e0, v2}
4590   cone(e0) = {v0, v1}
4591 .ve
4592   If `DMPlexStratify()` is run on this mesh, it will give depths
4593 .vb
4594    depth 0 = {v0, v1, v2}
4595    depth 1 = {e0, c0}
4596 .ve
4597   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4598 
4599   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4600 
4601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4602 @*/
4603 PetscErrorCode DMPlexStratify(DM dm)
4604 {
4605   DM_Plex  *mesh = (DM_Plex *)dm->data;
4606   DMLabel   label;
4607   PetscBool flg = PETSC_FALSE;
4608 
4609   PetscFunctionBegin;
4610   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4611   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4612 
4613   // Create depth label
4614   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4615   PetscCall(DMCreateLabel(dm, "depth"));
4616   PetscCall(DMPlexGetDepthLabel(dm, &label));
4617 
4618   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4619   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4620   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4621 
4622   { /* just in case there is an empty process */
4623     PetscInt numValues, maxValues = 0, v;
4624 
4625     PetscCall(DMLabelGetNumValues(label, &numValues));
4626     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4627     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4628   }
4629   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4630   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4631   PetscFunctionReturn(PETSC_SUCCESS);
4632 }
4633 
4634 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4635 {
4636   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4637   PetscInt       dim, depth, pheight, coneSize;
4638   PetscBool      preferTensor;
4639 
4640   PetscFunctionBeginHot;
4641   PetscCall(DMGetDimension(dm, &dim));
4642   PetscCall(DMPlexGetDepth(dm, &depth));
4643   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4644   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4645   pheight = depth - pdepth;
4646   if (depth <= 1) {
4647     switch (pdepth) {
4648     case 0:
4649       ct = DM_POLYTOPE_POINT;
4650       break;
4651     case 1:
4652       switch (coneSize) {
4653       case 2:
4654         ct = DM_POLYTOPE_SEGMENT;
4655         break;
4656       case 3:
4657         ct = DM_POLYTOPE_TRIANGLE;
4658         break;
4659       case 4:
4660         switch (dim) {
4661         case 2:
4662           ct = DM_POLYTOPE_QUADRILATERAL;
4663           break;
4664         case 3:
4665           ct = DM_POLYTOPE_TETRAHEDRON;
4666           break;
4667         default:
4668           break;
4669         }
4670         break;
4671       case 5:
4672         ct = DM_POLYTOPE_PYRAMID;
4673         break;
4674       case 6:
4675         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4676         break;
4677       case 8:
4678         ct = DM_POLYTOPE_HEXAHEDRON;
4679         break;
4680       default:
4681         break;
4682       }
4683     }
4684   } else {
4685     if (pdepth == 0) {
4686       ct = DM_POLYTOPE_POINT;
4687     } else if (pheight == 0) {
4688       switch (dim) {
4689       case 1:
4690         switch (coneSize) {
4691         case 2:
4692           ct = DM_POLYTOPE_SEGMENT;
4693           break;
4694         default:
4695           break;
4696         }
4697         break;
4698       case 2:
4699         switch (coneSize) {
4700         case 3:
4701           ct = DM_POLYTOPE_TRIANGLE;
4702           break;
4703         case 4:
4704           ct = DM_POLYTOPE_QUADRILATERAL;
4705           break;
4706         default:
4707           break;
4708         }
4709         break;
4710       case 3:
4711         switch (coneSize) {
4712         case 4:
4713           ct = DM_POLYTOPE_TETRAHEDRON;
4714           break;
4715         case 5: {
4716           const PetscInt *cone;
4717           PetscInt        faceConeSize;
4718 
4719           PetscCall(DMPlexGetCone(dm, p, &cone));
4720           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4721           switch (faceConeSize) {
4722           case 3:
4723             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4724             break;
4725           case 4:
4726             ct = DM_POLYTOPE_PYRAMID;
4727             break;
4728           }
4729         } break;
4730         case 6:
4731           ct = DM_POLYTOPE_HEXAHEDRON;
4732           break;
4733         default:
4734           break;
4735         }
4736         break;
4737       default:
4738         break;
4739       }
4740     } else if (pheight > 0) {
4741       switch (coneSize) {
4742       case 2:
4743         ct = DM_POLYTOPE_SEGMENT;
4744         break;
4745       case 3:
4746         ct = DM_POLYTOPE_TRIANGLE;
4747         break;
4748       case 4:
4749         ct = DM_POLYTOPE_QUADRILATERAL;
4750         break;
4751       default:
4752         break;
4753       }
4754     }
4755   }
4756   *pt = ct;
4757   PetscFunctionReturn(PETSC_SUCCESS);
4758 }
4759 
4760 /*@
4761   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4762 
4763   Collective
4764 
4765   Input Parameter:
4766 . dm - The `DMPLEX`
4767 
4768   Level: developer
4769 
4770   Note:
4771   This function is normally called automatically when a cell type is requested. It creates an
4772   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4773   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4774 
4775   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4776 
4777 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4778 @*/
4779 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4780 {
4781   DM_Plex *mesh;
4782   DMLabel  ctLabel;
4783   PetscInt pStart, pEnd, p;
4784 
4785   PetscFunctionBegin;
4786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4787   mesh = (DM_Plex *)dm->data;
4788   PetscCall(DMCreateLabel(dm, "celltype"));
4789   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4790   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4791   PetscCall(PetscFree(mesh->cellTypes));
4792   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4793   for (p = pStart; p < pEnd; ++p) {
4794     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4795     PetscInt       pdepth;
4796 
4797     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4798     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4799     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4800     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4801     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4802   }
4803   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4804   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4805   PetscFunctionReturn(PETSC_SUCCESS);
4806 }
4807 
4808 /*@C
4809   DMPlexGetJoin - Get an array for the join of the set of points
4810 
4811   Not Collective
4812 
4813   Input Parameters:
4814 + dm        - The `DMPLEX` object
4815 . numPoints - The number of input points for the join
4816 - points    - The input points
4817 
4818   Output Parameters:
4819 + numCoveredPoints - The number of points in the join
4820 - coveredPoints    - The points in the join
4821 
4822   Level: intermediate
4823 
4824   Note:
4825   Currently, this is restricted to a single level join
4826 
4827   Fortran Notes:
4828   `converedPoints` must be declared with
4829 .vb
4830   PetscInt, pointer :: coveredPints(:)
4831 .ve
4832 
4833 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4834 @*/
4835 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4836 {
4837   DM_Plex  *mesh = (DM_Plex *)dm->data;
4838   PetscInt *join[2];
4839   PetscInt  joinSize, i = 0;
4840   PetscInt  dof, off, p, c, m;
4841   PetscInt  maxSupportSize;
4842 
4843   PetscFunctionBegin;
4844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4845   PetscAssertPointer(points, 3);
4846   PetscAssertPointer(numCoveredPoints, 4);
4847   PetscAssertPointer(coveredPoints, 5);
4848   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4849   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4850   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4851   /* Copy in support of first point */
4852   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4853   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4854   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4855   /* Check each successive support */
4856   for (p = 1; p < numPoints; ++p) {
4857     PetscInt newJoinSize = 0;
4858 
4859     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4860     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4861     for (c = 0; c < dof; ++c) {
4862       const PetscInt point = mesh->supports[off + c];
4863 
4864       for (m = 0; m < joinSize; ++m) {
4865         if (point == join[i][m]) {
4866           join[1 - i][newJoinSize++] = point;
4867           break;
4868         }
4869       }
4870     }
4871     joinSize = newJoinSize;
4872     i        = 1 - i;
4873   }
4874   *numCoveredPoints = joinSize;
4875   *coveredPoints    = join[i];
4876   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4877   PetscFunctionReturn(PETSC_SUCCESS);
4878 }
4879 
4880 /*@C
4881   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4882 
4883   Not Collective
4884 
4885   Input Parameters:
4886 + dm        - The `DMPLEX` object
4887 . numPoints - The number of input points for the join
4888 - points    - The input points
4889 
4890   Output Parameters:
4891 + numCoveredPoints - The number of points in the join
4892 - coveredPoints    - The points in the join
4893 
4894   Level: intermediate
4895 
4896   Fortran Notes:
4897   `converedPoints` must be declared with
4898 .vb
4899   PetscInt, pointer :: coveredPoints(:)
4900 .ve
4901 
4902   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4903 
4904 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4905 @*/
4906 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4907 {
4908   PetscFunctionBegin;
4909   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4910   if (points) PetscAssertPointer(points, 3);
4911   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4912   PetscAssertPointer(coveredPoints, 5);
4913   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4914   if (numCoveredPoints) *numCoveredPoints = 0;
4915   PetscFunctionReturn(PETSC_SUCCESS);
4916 }
4917 
4918 /*@C
4919   DMPlexGetFullJoin - Get an array for the join of the set of points
4920 
4921   Not Collective
4922 
4923   Input Parameters:
4924 + dm        - The `DMPLEX` object
4925 . numPoints - The number of input points for the join
4926 - points    - The input points, its length is `numPoints`
4927 
4928   Output Parameters:
4929 + numCoveredPoints - The number of points in the join
4930 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4931 
4932   Level: intermediate
4933 
4934   Fortran Notes:
4935 .vb
4936   PetscInt, pointer :: coveredPints(:)
4937 .ve
4938 
4939 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4940 @*/
4941 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4942 {
4943   PetscInt *offsets, **closures;
4944   PetscInt *join[2];
4945   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4946   PetscInt  p, d, c, m, ms;
4947 
4948   PetscFunctionBegin;
4949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4950   PetscAssertPointer(points, 3);
4951   PetscAssertPointer(numCoveredPoints, 4);
4952   PetscAssertPointer(coveredPoints, 5);
4953 
4954   PetscCall(DMPlexGetDepth(dm, &depth));
4955   PetscCall(PetscCalloc1(numPoints, &closures));
4956   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4957   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4958   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4959   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4960   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4961 
4962   for (p = 0; p < numPoints; ++p) {
4963     PetscInt closureSize;
4964 
4965     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4966 
4967     offsets[p * (depth + 2) + 0] = 0;
4968     for (d = 0; d < depth + 1; ++d) {
4969       PetscInt pStart, pEnd, i;
4970 
4971       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4972       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4973         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4974           offsets[p * (depth + 2) + d + 1] = i;
4975           break;
4976         }
4977       }
4978       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4979     }
4980     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);
4981   }
4982   for (d = 0; d < depth + 1; ++d) {
4983     PetscInt dof;
4984 
4985     /* Copy in support of first point */
4986     dof = offsets[d + 1] - offsets[d];
4987     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4988     /* Check each successive cone */
4989     for (p = 1; p < numPoints && joinSize; ++p) {
4990       PetscInt newJoinSize = 0;
4991 
4992       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4993       for (c = 0; c < dof; ++c) {
4994         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4995 
4996         for (m = 0; m < joinSize; ++m) {
4997           if (point == join[i][m]) {
4998             join[1 - i][newJoinSize++] = point;
4999             break;
5000           }
5001         }
5002       }
5003       joinSize = newJoinSize;
5004       i        = 1 - i;
5005     }
5006     if (joinSize) break;
5007   }
5008   *numCoveredPoints = joinSize;
5009   *coveredPoints    = join[i];
5010   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5011   PetscCall(PetscFree(closures));
5012   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5013   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5014   PetscFunctionReturn(PETSC_SUCCESS);
5015 }
5016 
5017 /*@C
5018   DMPlexGetMeet - Get an array for the meet of the set of points
5019 
5020   Not Collective
5021 
5022   Input Parameters:
5023 + dm        - The `DMPLEX` object
5024 . numPoints - The number of input points for the meet
5025 - points    - The input points, of length `numPoints`
5026 
5027   Output Parameters:
5028 + numCoveringPoints - The number of points in the meet
5029 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5030 
5031   Level: intermediate
5032 
5033   Note:
5034   Currently, this is restricted to a single level meet
5035 
5036   Fortran Note:
5037   `coveringPoints` must be declared with
5038 .vb
5039   PetscInt, pointer :: coveringPoints(:)
5040 .ve
5041 
5042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5043 @*/
5044 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5045 {
5046   DM_Plex  *mesh = (DM_Plex *)dm->data;
5047   PetscInt *meet[2];
5048   PetscInt  meetSize, i = 0;
5049   PetscInt  dof, off, p, c, m;
5050   PetscInt  maxConeSize;
5051 
5052   PetscFunctionBegin;
5053   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5054   PetscAssertPointer(points, 3);
5055   PetscAssertPointer(numCoveringPoints, 4);
5056   PetscAssertPointer(coveringPoints, 5);
5057   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5058   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5059   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5060   /* Copy in cone of first point */
5061   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5062   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5063   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5064   /* Check each successive cone */
5065   for (p = 1; p < numPoints; ++p) {
5066     PetscInt newMeetSize = 0;
5067 
5068     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5069     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5070     for (c = 0; c < dof; ++c) {
5071       const PetscInt point = mesh->cones[off + c];
5072 
5073       for (m = 0; m < meetSize; ++m) {
5074         if (point == meet[i][m]) {
5075           meet[1 - i][newMeetSize++] = point;
5076           break;
5077         }
5078       }
5079     }
5080     meetSize = newMeetSize;
5081     i        = 1 - i;
5082   }
5083   *numCoveringPoints = meetSize;
5084   *coveringPoints    = meet[i];
5085   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5086   PetscFunctionReturn(PETSC_SUCCESS);
5087 }
5088 
5089 /*@C
5090   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5091 
5092   Not Collective
5093 
5094   Input Parameters:
5095 + dm        - The `DMPLEX` object
5096 . numPoints - The number of input points for the meet
5097 - points    - The input points
5098 
5099   Output Parameters:
5100 + numCoveredPoints - The number of points in the meet
5101 - coveredPoints    - The points in the meet
5102 
5103   Level: intermediate
5104 
5105 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5106 @*/
5107 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5108 {
5109   PetscFunctionBegin;
5110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5111   if (points) PetscAssertPointer(points, 3);
5112   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5113   PetscAssertPointer(coveredPoints, 5);
5114   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5115   if (numCoveredPoints) *numCoveredPoints = 0;
5116   PetscFunctionReturn(PETSC_SUCCESS);
5117 }
5118 
5119 /*@C
5120   DMPlexGetFullMeet - Get an array for the meet of the set of points
5121 
5122   Not Collective
5123 
5124   Input Parameters:
5125 + dm        - The `DMPLEX` object
5126 . numPoints - The number of input points for the meet
5127 - points    - The input points, of length  `numPoints`
5128 
5129   Output Parameters:
5130 + numCoveredPoints - The number of points in the meet
5131 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5132 
5133   Level: intermediate
5134 
5135   Fortran Notes:
5136   `coveredPoints` must be declared with
5137 .vb
5138   PetscInt, pointer :: coveredPoints(:)
5139 .ve
5140 
5141 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5142 @*/
5143 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5144 {
5145   PetscInt *offsets, **closures;
5146   PetscInt *meet[2];
5147   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5148   PetscInt  p, h, c, m, mc;
5149 
5150   PetscFunctionBegin;
5151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5152   PetscAssertPointer(points, 3);
5153   PetscAssertPointer(numCoveredPoints, 4);
5154   PetscAssertPointer(coveredPoints, 5);
5155 
5156   PetscCall(DMPlexGetDepth(dm, &height));
5157   PetscCall(PetscMalloc1(numPoints, &closures));
5158   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5159   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5160   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5161   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5162   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5163 
5164   for (p = 0; p < numPoints; ++p) {
5165     PetscInt closureSize;
5166 
5167     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5168 
5169     offsets[p * (height + 2) + 0] = 0;
5170     for (h = 0; h < height + 1; ++h) {
5171       PetscInt pStart, pEnd, i;
5172 
5173       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5174       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5175         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5176           offsets[p * (height + 2) + h + 1] = i;
5177           break;
5178         }
5179       }
5180       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5181     }
5182     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);
5183   }
5184   for (h = 0; h < height + 1; ++h) {
5185     PetscInt dof;
5186 
5187     /* Copy in cone of first point */
5188     dof = offsets[h + 1] - offsets[h];
5189     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5190     /* Check each successive cone */
5191     for (p = 1; p < numPoints && meetSize; ++p) {
5192       PetscInt newMeetSize = 0;
5193 
5194       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5195       for (c = 0; c < dof; ++c) {
5196         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5197 
5198         for (m = 0; m < meetSize; ++m) {
5199           if (point == meet[i][m]) {
5200             meet[1 - i][newMeetSize++] = point;
5201             break;
5202           }
5203         }
5204       }
5205       meetSize = newMeetSize;
5206       i        = 1 - i;
5207     }
5208     if (meetSize) break;
5209   }
5210   *numCoveredPoints = meetSize;
5211   *coveredPoints    = meet[i];
5212   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5213   PetscCall(PetscFree(closures));
5214   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5215   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5216   PetscFunctionReturn(PETSC_SUCCESS);
5217 }
5218 
5219 /*@
5220   DMPlexEqual - Determine if two `DM` have the same topology
5221 
5222   Not Collective
5223 
5224   Input Parameters:
5225 + dmA - A `DMPLEX` object
5226 - dmB - A `DMPLEX` object
5227 
5228   Output Parameter:
5229 . equal - `PETSC_TRUE` if the topologies are identical
5230 
5231   Level: intermediate
5232 
5233   Note:
5234   We are not solving graph isomorphism, so we do not permute.
5235 
5236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5237 @*/
5238 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5239 {
5240   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5241 
5242   PetscFunctionBegin;
5243   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5244   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5245   PetscAssertPointer(equal, 3);
5246 
5247   *equal = PETSC_FALSE;
5248   PetscCall(DMPlexGetDepth(dmA, &depth));
5249   PetscCall(DMPlexGetDepth(dmB, &depthB));
5250   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5251   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5252   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5253   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5254   for (p = pStart; p < pEnd; ++p) {
5255     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5256     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5257 
5258     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5259     PetscCall(DMPlexGetCone(dmA, p, &cone));
5260     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5261     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5262     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5263     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5264     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5265     for (c = 0; c < coneSize; ++c) {
5266       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5267       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5268     }
5269     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5270     PetscCall(DMPlexGetSupport(dmA, p, &support));
5271     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5272     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5273     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5274     for (s = 0; s < supportSize; ++s) {
5275       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5276     }
5277   }
5278   *equal = PETSC_TRUE;
5279   PetscFunctionReturn(PETSC_SUCCESS);
5280 }
5281 
5282 /*@
5283   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5284 
5285   Not Collective
5286 
5287   Input Parameters:
5288 + dm         - The `DMPLEX`
5289 . cellDim    - The cell dimension
5290 - numCorners - The number of vertices on a cell
5291 
5292   Output Parameter:
5293 . numFaceVertices - The number of vertices on a face
5294 
5295   Level: developer
5296 
5297   Note:
5298   Of course this can only work for a restricted set of symmetric shapes
5299 
5300 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5301 @*/
5302 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5303 {
5304   MPI_Comm comm;
5305 
5306   PetscFunctionBegin;
5307   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5308   PetscAssertPointer(numFaceVertices, 4);
5309   switch (cellDim) {
5310   case 0:
5311     *numFaceVertices = 0;
5312     break;
5313   case 1:
5314     *numFaceVertices = 1;
5315     break;
5316   case 2:
5317     switch (numCorners) {
5318     case 3:                 /* triangle */
5319       *numFaceVertices = 2; /* Edge has 2 vertices */
5320       break;
5321     case 4:                 /* quadrilateral */
5322       *numFaceVertices = 2; /* Edge has 2 vertices */
5323       break;
5324     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5325       *numFaceVertices = 3; /* Edge has 3 vertices */
5326       break;
5327     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5328       *numFaceVertices = 3; /* Edge has 3 vertices */
5329       break;
5330     default:
5331       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5332     }
5333     break;
5334   case 3:
5335     switch (numCorners) {
5336     case 4:                 /* tetradehdron */
5337       *numFaceVertices = 3; /* Face has 3 vertices */
5338       break;
5339     case 6:                 /* tet cohesive cells */
5340       *numFaceVertices = 4; /* Face has 4 vertices */
5341       break;
5342     case 8:                 /* hexahedron */
5343       *numFaceVertices = 4; /* Face has 4 vertices */
5344       break;
5345     case 9:                 /* tet cohesive Lagrange cells */
5346       *numFaceVertices = 6; /* Face has 6 vertices */
5347       break;
5348     case 10:                /* quadratic tetrahedron */
5349       *numFaceVertices = 6; /* Face has 6 vertices */
5350       break;
5351     case 12:                /* hex cohesive Lagrange cells */
5352       *numFaceVertices = 6; /* Face has 6 vertices */
5353       break;
5354     case 18:                /* quadratic tet cohesive Lagrange cells */
5355       *numFaceVertices = 6; /* Face has 6 vertices */
5356       break;
5357     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5358       *numFaceVertices = 9; /* Face has 9 vertices */
5359       break;
5360     default:
5361       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5362     }
5363     break;
5364   default:
5365     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5366   }
5367   PetscFunctionReturn(PETSC_SUCCESS);
5368 }
5369 
5370 /*@
5371   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5372 
5373   Not Collective
5374 
5375   Input Parameter:
5376 . dm - The `DMPLEX` object
5377 
5378   Output Parameter:
5379 . depthLabel - The `DMLabel` recording point depth
5380 
5381   Level: developer
5382 
5383 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5384 @*/
5385 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5386 {
5387   PetscFunctionBegin;
5388   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5389   PetscAssertPointer(depthLabel, 2);
5390   *depthLabel = dm->depthLabel;
5391   PetscFunctionReturn(PETSC_SUCCESS);
5392 }
5393 
5394 /*@
5395   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5396 
5397   Not Collective
5398 
5399   Input Parameter:
5400 . dm - The `DMPLEX` object
5401 
5402   Output Parameter:
5403 . depth - The number of strata (breadth first levels) in the DAG
5404 
5405   Level: developer
5406 
5407   Notes:
5408   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5409 
5410   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5411 
5412   An empty mesh gives -1.
5413 
5414 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5415 @*/
5416 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5417 {
5418   DM_Plex *mesh = (DM_Plex *)dm->data;
5419   DMLabel  label;
5420   PetscInt d = -1;
5421 
5422   PetscFunctionBegin;
5423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5424   PetscAssertPointer(depth, 2);
5425   if (mesh->tr) {
5426     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5427   } else {
5428     PetscCall(DMPlexGetDepthLabel(dm, &label));
5429     // Allow missing depths
5430     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5431     *depth = d;
5432   }
5433   PetscFunctionReturn(PETSC_SUCCESS);
5434 }
5435 
5436 /*@
5437   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5438 
5439   Not Collective
5440 
5441   Input Parameters:
5442 + dm    - The `DMPLEX` object
5443 - depth - The requested depth
5444 
5445   Output Parameters:
5446 + start - The first point at this `depth`
5447 - end   - One beyond the last point at this `depth`
5448 
5449   Level: developer
5450 
5451   Notes:
5452   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5453   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5454   higher dimension, e.g., "edges".
5455 
5456 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5457 @*/
5458 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5459 {
5460   DM_Plex *mesh = (DM_Plex *)dm->data;
5461   DMLabel  label;
5462   PetscInt pStart, pEnd;
5463 
5464   PetscFunctionBegin;
5465   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5466   if (start) {
5467     PetscAssertPointer(start, 3);
5468     *start = 0;
5469   }
5470   if (end) {
5471     PetscAssertPointer(end, 4);
5472     *end = 0;
5473   }
5474   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5475   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5476   if (depth < 0) {
5477     if (start) *start = pStart;
5478     if (end) *end = pEnd;
5479     PetscFunctionReturn(PETSC_SUCCESS);
5480   }
5481   if (mesh->tr) {
5482     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5483   } else {
5484     PetscCall(DMPlexGetDepthLabel(dm, &label));
5485     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5486     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5487   }
5488   PetscFunctionReturn(PETSC_SUCCESS);
5489 }
5490 
5491 /*@
5492   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5493 
5494   Not Collective
5495 
5496   Input Parameters:
5497 + dm     - The `DMPLEX` object
5498 - height - The requested height
5499 
5500   Output Parameters:
5501 + start - The first point at this `height`
5502 - end   - One beyond the last point at this `height`
5503 
5504   Level: developer
5505 
5506   Notes:
5507   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5508   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5509   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5510 
5511 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5512 @*/
5513 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5514 {
5515   DMLabel  label;
5516   PetscInt depth, pStart, pEnd;
5517 
5518   PetscFunctionBegin;
5519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5520   if (start) {
5521     PetscAssertPointer(start, 3);
5522     *start = 0;
5523   }
5524   if (end) {
5525     PetscAssertPointer(end, 4);
5526     *end = 0;
5527   }
5528   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5529   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5530   if (height < 0) {
5531     if (start) *start = pStart;
5532     if (end) *end = pEnd;
5533     PetscFunctionReturn(PETSC_SUCCESS);
5534   }
5535   PetscCall(DMPlexGetDepthLabel(dm, &label));
5536   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5537   else PetscCall(DMGetDimension(dm, &depth));
5538   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5539   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5540   PetscFunctionReturn(PETSC_SUCCESS);
5541 }
5542 
5543 /*@
5544   DMPlexGetPointDepth - Get the `depth` of a given point
5545 
5546   Not Collective
5547 
5548   Input Parameters:
5549 + dm    - The `DMPLEX` object
5550 - point - The point
5551 
5552   Output Parameter:
5553 . depth - The depth of the `point`
5554 
5555   Level: intermediate
5556 
5557 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5558 @*/
5559 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5560 {
5561   PetscFunctionBegin;
5562   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5563   PetscAssertPointer(depth, 3);
5564   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5565   PetscFunctionReturn(PETSC_SUCCESS);
5566 }
5567 
5568 /*@
5569   DMPlexGetPointHeight - Get the `height` of a given point
5570 
5571   Not Collective
5572 
5573   Input Parameters:
5574 + dm    - The `DMPLEX` object
5575 - point - The point
5576 
5577   Output Parameter:
5578 . height - The height of the `point`
5579 
5580   Level: intermediate
5581 
5582 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5583 @*/
5584 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5585 {
5586   PetscInt n, pDepth;
5587 
5588   PetscFunctionBegin;
5589   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5590   PetscAssertPointer(height, 3);
5591   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5592   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5593   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5594   PetscFunctionReturn(PETSC_SUCCESS);
5595 }
5596 
5597 /*@
5598   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5599 
5600   Not Collective
5601 
5602   Input Parameter:
5603 . dm - The `DMPLEX` object
5604 
5605   Output Parameter:
5606 . celltypeLabel - The `DMLabel` recording cell polytope type
5607 
5608   Level: developer
5609 
5610   Note:
5611   This function will trigger automatica computation of cell types. This can be disabled by calling
5612   `DMCreateLabel`(dm, "celltype") beforehand.
5613 
5614 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5615 @*/
5616 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5617 {
5618   PetscFunctionBegin;
5619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5620   PetscAssertPointer(celltypeLabel, 2);
5621   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5622   *celltypeLabel = dm->celltypeLabel;
5623   PetscFunctionReturn(PETSC_SUCCESS);
5624 }
5625 
5626 /*@
5627   DMPlexGetCellType - Get the polytope type of a given cell
5628 
5629   Not Collective
5630 
5631   Input Parameters:
5632 + dm   - The `DMPLEX` object
5633 - cell - The cell
5634 
5635   Output Parameter:
5636 . celltype - The polytope type of the cell
5637 
5638   Level: intermediate
5639 
5640 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5641 @*/
5642 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5643 {
5644   DM_Plex *mesh = (DM_Plex *)dm->data;
5645   DMLabel  label;
5646   PetscInt ct;
5647 
5648   PetscFunctionBegin;
5649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5650   PetscAssertPointer(celltype, 3);
5651   if (mesh->tr) {
5652     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5653   } else {
5654     PetscInt pStart, pEnd;
5655 
5656     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5657     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5658       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5659       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5660       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5661       for (PetscInt p = pStart; p < pEnd; p++) {
5662         PetscCall(DMLabelGetValue(label, p, &ct));
5663         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5664       }
5665     }
5666     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5667     if (PetscDefined(USE_DEBUG)) {
5668       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5669       PetscCall(DMLabelGetValue(label, cell, &ct));
5670       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5671       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5672     }
5673   }
5674   PetscFunctionReturn(PETSC_SUCCESS);
5675 }
5676 
5677 /*@
5678   DMPlexSetCellType - Set the polytope type of a given cell
5679 
5680   Not Collective
5681 
5682   Input Parameters:
5683 + dm       - The `DMPLEX` object
5684 . cell     - The cell
5685 - celltype - The polytope type of the cell
5686 
5687   Level: advanced
5688 
5689   Note:
5690   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5691   is executed. This function will override the computed type. However, if automatic classification will not succeed
5692   and a user wants to manually specify all types, the classification must be disabled by calling
5693   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5694 
5695 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5696 @*/
5697 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5698 {
5699   DM_Plex *mesh = (DM_Plex *)dm->data;
5700   DMLabel  label;
5701   PetscInt pStart, pEnd;
5702 
5703   PetscFunctionBegin;
5704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5705   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5706   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5707   PetscCall(DMLabelSetValue(label, cell, celltype));
5708   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5709   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5710   PetscFunctionReturn(PETSC_SUCCESS);
5711 }
5712 
5713 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5714 {
5715   PetscSection section;
5716   PetscInt     maxHeight;
5717   const char  *prefix;
5718 
5719   PetscFunctionBegin;
5720   PetscCall(DMClone(dm, cdm));
5721   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5722   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5723   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5724   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5725   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5726   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5727   PetscCall(DMSetLocalSection(*cdm, section));
5728   PetscCall(PetscSectionDestroy(&section));
5729 
5730   PetscCall(DMSetNumFields(*cdm, 1));
5731   PetscCall(DMCreateDS(*cdm));
5732   (*cdm)->cloneOpts = PETSC_TRUE;
5733   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5734   PetscFunctionReturn(PETSC_SUCCESS);
5735 }
5736 
5737 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5738 {
5739   Vec coordsLocal, cellCoordsLocal;
5740   DM  coordsDM, cellCoordsDM;
5741 
5742   PetscFunctionBegin;
5743   *field = NULL;
5744   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5745   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5746   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5747   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5748   if (coordsLocal && coordsDM) {
5749     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5750     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5751   }
5752   PetscFunctionReturn(PETSC_SUCCESS);
5753 }
5754 
5755 /*@
5756   DMPlexGetConeSection - Return a section which describes the layout of cone data
5757 
5758   Not Collective
5759 
5760   Input Parameter:
5761 . dm - The `DMPLEX` object
5762 
5763   Output Parameter:
5764 . section - The `PetscSection` object
5765 
5766   Level: developer
5767 
5768 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5769 @*/
5770 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5771 {
5772   DM_Plex *mesh = (DM_Plex *)dm->data;
5773 
5774   PetscFunctionBegin;
5775   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5776   if (section) *section = mesh->coneSection;
5777   PetscFunctionReturn(PETSC_SUCCESS);
5778 }
5779 
5780 /*@
5781   DMPlexGetSupportSection - Return a section which describes the layout of support data
5782 
5783   Not Collective
5784 
5785   Input Parameter:
5786 . dm - The `DMPLEX` object
5787 
5788   Output Parameter:
5789 . section - The `PetscSection` object
5790 
5791   Level: developer
5792 
5793 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5794 @*/
5795 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5796 {
5797   DM_Plex *mesh = (DM_Plex *)dm->data;
5798 
5799   PetscFunctionBegin;
5800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5801   if (section) *section = mesh->supportSection;
5802   PetscFunctionReturn(PETSC_SUCCESS);
5803 }
5804 
5805 /*@C
5806   DMPlexGetCones - Return cone data
5807 
5808   Not Collective
5809 
5810   Input Parameter:
5811 . dm - The `DMPLEX` object
5812 
5813   Output Parameter:
5814 . cones - The cone for each point
5815 
5816   Level: developer
5817 
5818 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5819 @*/
5820 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5821 {
5822   DM_Plex *mesh = (DM_Plex *)dm->data;
5823 
5824   PetscFunctionBegin;
5825   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5826   if (cones) *cones = mesh->cones;
5827   PetscFunctionReturn(PETSC_SUCCESS);
5828 }
5829 
5830 /*@C
5831   DMPlexGetConeOrientations - Return cone orientation data
5832 
5833   Not Collective
5834 
5835   Input Parameter:
5836 . dm - The `DMPLEX` object
5837 
5838   Output Parameter:
5839 . coneOrientations - The array of cone orientations for all points
5840 
5841   Level: developer
5842 
5843   Notes:
5844   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5845   as returned by `DMPlexGetConeOrientation()`.
5846 
5847   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5848 
5849 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5850 @*/
5851 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5852 {
5853   DM_Plex *mesh = (DM_Plex *)dm->data;
5854 
5855   PetscFunctionBegin;
5856   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5857   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5858   PetscFunctionReturn(PETSC_SUCCESS);
5859 }
5860 
5861 /* FEM Support */
5862 
5863 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5864 {
5865   PetscInt depth;
5866 
5867   PetscFunctionBegin;
5868   PetscCall(DMPlexGetDepth(plex, &depth));
5869   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5870   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5871   PetscFunctionReturn(PETSC_SUCCESS);
5872 }
5873 
5874 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5875 {
5876   PetscInt depth;
5877 
5878   PetscFunctionBegin;
5879   PetscCall(DMPlexGetDepth(plex, &depth));
5880   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5881   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5882   PetscFunctionReturn(PETSC_SUCCESS);
5883 }
5884 
5885 /*
5886  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5887  representing a line in the section.
5888 */
5889 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5890 {
5891   PetscObject  obj;
5892   PetscClassId id;
5893   PetscFE      fe = NULL;
5894 
5895   PetscFunctionBeginHot;
5896   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5897   PetscCall(DMGetField(dm, field, NULL, &obj));
5898   PetscCall(PetscObjectGetClassId(obj, &id));
5899   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5900 
5901   if (!fe) {
5902     /* Assume the full interpolated mesh is in the chart; lines in particular */
5903     /* An order k SEM disc has k-1 dofs on an edge */
5904     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5905     *k = *k / *Nc + 1;
5906   } else {
5907     PetscInt       dual_space_size, dim;
5908     PetscDualSpace dsp;
5909 
5910     PetscCall(DMGetDimension(dm, &dim));
5911     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5912     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5913     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5914     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5915     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5916   }
5917   PetscFunctionReturn(PETSC_SUCCESS);
5918 }
5919 
5920 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5921 {
5922   PetscFunctionBeginHot;
5923   if (tensor) {
5924     *dof = PetscPowInt(k + 1, dim);
5925   } else {
5926     switch (dim) {
5927     case 1:
5928       *dof = k + 1;
5929       break;
5930     case 2:
5931       *dof = ((k + 1) * (k + 2)) / 2;
5932       break;
5933     case 3:
5934       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5935       break;
5936     default:
5937       *dof = 0;
5938     }
5939   }
5940   PetscFunctionReturn(PETSC_SUCCESS);
5941 }
5942 
5943 /*@
5944   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5945   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5946   section provided (or the section of the `DM`).
5947 
5948   Input Parameters:
5949 + dm      - The `DM`
5950 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5951 - section - The `PetscSection` to reorder, or `NULL` for the default section
5952 
5953   Example:
5954   A typical interpolated single-quad mesh might order points as
5955 .vb
5956   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5957 
5958   v4 -- e6 -- v3
5959   |           |
5960   e7    c0    e8
5961   |           |
5962   v1 -- e5 -- v2
5963 .ve
5964 
5965   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5966   dofs in the order of points, e.g.,
5967 .vb
5968     c0 -> [0,1,2,3]
5969     v1 -> [4]
5970     ...
5971     e5 -> [8, 9]
5972 .ve
5973 
5974   which corresponds to the dofs
5975 .vb
5976     6   10  11  7
5977     13  2   3   15
5978     12  0   1   14
5979     4   8   9   5
5980 .ve
5981 
5982   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5983 .vb
5984   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5985 .ve
5986 
5987   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5988 .vb
5989    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5990 .ve
5991 
5992   Level: developer
5993 
5994   Notes:
5995   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5996   degree of the basis.
5997 
5998   This is required to run with libCEED.
5999 
6000 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6001 @*/
6002 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6003 {
6004   DMLabel   label;
6005   PetscInt  dim, depth = -1, eStart = -1, Nf;
6006   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6007 
6008   PetscFunctionBegin;
6009   PetscCall(DMGetDimension(dm, &dim));
6010   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6011   if (point < 0) {
6012     PetscInt sStart, sEnd;
6013 
6014     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6015     point = sEnd - sStart ? sStart : point;
6016   }
6017   PetscCall(DMPlexGetDepthLabel(dm, &label));
6018   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6019   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6020   if (depth == 1) {
6021     eStart = point;
6022   } else if (depth == dim) {
6023     const PetscInt *cone;
6024 
6025     PetscCall(DMPlexGetCone(dm, point, &cone));
6026     if (dim == 2) eStart = cone[0];
6027     else if (dim == 3) {
6028       const PetscInt *cone2;
6029       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6030       eStart = cone2[0];
6031     } 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);
6032   } 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);
6033 
6034   PetscCall(PetscSectionGetNumFields(section, &Nf));
6035   for (PetscInt d = 1; d <= dim; d++) {
6036     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6037     PetscInt *perm;
6038 
6039     for (f = 0; f < Nf; ++f) {
6040       PetscInt dof;
6041 
6042       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6043       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6044       if (!continuous && d < dim) continue;
6045       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6046       size += dof * Nc;
6047     }
6048     PetscCall(PetscMalloc1(size, &perm));
6049     for (f = 0; f < Nf; ++f) {
6050       switch (d) {
6051       case 1:
6052         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6053         if (!continuous && d < dim) continue;
6054         /*
6055          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6056          We want              [ vtx0; edge of length k-1; vtx1 ]
6057          */
6058         if (continuous) {
6059           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6060           for (i = 0; i < k - 1; i++)
6061             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6062           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6063           foffset = offset;
6064         } else {
6065           PetscInt dof;
6066 
6067           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6068           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6069           foffset = offset;
6070         }
6071         break;
6072       case 2:
6073         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6074         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6075         if (!continuous && d < dim) continue;
6076         /* The SEM order is
6077 
6078          v_lb, {e_b}, v_rb,
6079          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6080          v_lt, reverse {e_t}, v_rt
6081          */
6082         if (continuous) {
6083           const PetscInt of   = 0;
6084           const PetscInt oeb  = of + PetscSqr(k - 1);
6085           const PetscInt oer  = oeb + (k - 1);
6086           const PetscInt oet  = oer + (k - 1);
6087           const PetscInt oel  = oet + (k - 1);
6088           const PetscInt ovlb = oel + (k - 1);
6089           const PetscInt ovrb = ovlb + 1;
6090           const PetscInt ovrt = ovrb + 1;
6091           const PetscInt ovlt = ovrt + 1;
6092           PetscInt       o;
6093 
6094           /* bottom */
6095           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6096           for (o = oeb; o < oer; ++o)
6097             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6098           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6099           /* middle */
6100           for (i = 0; i < k - 1; ++i) {
6101             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6102             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6103               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6104             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6105           }
6106           /* top */
6107           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6108           for (o = oel - 1; o >= oet; --o)
6109             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6110           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6111           foffset = offset;
6112         } else {
6113           PetscInt dof;
6114 
6115           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6116           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6117           foffset = offset;
6118         }
6119         break;
6120       case 3:
6121         /* The original hex closure is
6122 
6123          {c,
6124          f_b, f_t, f_f, f_b, f_r, f_l,
6125          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6126          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6127          */
6128         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6129         if (!continuous && d < dim) continue;
6130         /* The SEM order is
6131          Bottom Slice
6132          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6133          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6134          v_blb, {e_bb}, v_brb,
6135 
6136          Middle Slice (j)
6137          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6138          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6139          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6140 
6141          Top Slice
6142          v_tlf, {e_tf}, v_trf,
6143          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6144          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6145          */
6146         if (continuous) {
6147           const PetscInt oc    = 0;
6148           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6149           const PetscInt oft   = ofb + PetscSqr(k - 1);
6150           const PetscInt off   = oft + PetscSqr(k - 1);
6151           const PetscInt ofk   = off + PetscSqr(k - 1);
6152           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6153           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6154           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6155           const PetscInt oebb  = oebl + (k - 1);
6156           const PetscInt oebr  = oebb + (k - 1);
6157           const PetscInt oebf  = oebr + (k - 1);
6158           const PetscInt oetf  = oebf + (k - 1);
6159           const PetscInt oetr  = oetf + (k - 1);
6160           const PetscInt oetb  = oetr + (k - 1);
6161           const PetscInt oetl  = oetb + (k - 1);
6162           const PetscInt oerf  = oetl + (k - 1);
6163           const PetscInt oelf  = oerf + (k - 1);
6164           const PetscInt oelb  = oelf + (k - 1);
6165           const PetscInt oerb  = oelb + (k - 1);
6166           const PetscInt ovblf = oerb + (k - 1);
6167           const PetscInt ovblb = ovblf + 1;
6168           const PetscInt ovbrb = ovblb + 1;
6169           const PetscInt ovbrf = ovbrb + 1;
6170           const PetscInt ovtlf = ovbrf + 1;
6171           const PetscInt ovtrf = ovtlf + 1;
6172           const PetscInt ovtrb = ovtrf + 1;
6173           const PetscInt ovtlb = ovtrb + 1;
6174           PetscInt       o, n;
6175 
6176           /* Bottom Slice */
6177           /*   bottom */
6178           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6179           for (o = oetf - 1; o >= oebf; --o)
6180             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6181           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6182           /*   middle */
6183           for (i = 0; i < k - 1; ++i) {
6184             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6185             for (n = 0; n < k - 1; ++n) {
6186               o = ofb + n * (k - 1) + i;
6187               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6188             }
6189             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6190           }
6191           /*   top */
6192           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6193           for (o = oebb; o < oebr; ++o)
6194             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6195           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6196 
6197           /* Middle Slice */
6198           for (j = 0; j < k - 1; ++j) {
6199             /*   bottom */
6200             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6201             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6202               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6203             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6204             /*   middle */
6205             for (i = 0; i < k - 1; ++i) {
6206               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6207               for (n = 0; n < k - 1; ++n)
6208                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6209               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6210             }
6211             /*   top */
6212             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6213             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6214               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6215             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6216           }
6217 
6218           /* Top Slice */
6219           /*   bottom */
6220           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6221           for (o = oetf; o < oetr; ++o)
6222             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6223           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6224           /*   middle */
6225           for (i = 0; i < k - 1; ++i) {
6226             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6227             for (n = 0; n < k - 1; ++n)
6228               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6229             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6230           }
6231           /*   top */
6232           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6233           for (o = oetl - 1; o >= oetb; --o)
6234             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6235           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6236 
6237           foffset = offset;
6238         } else {
6239           PetscInt dof;
6240 
6241           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6242           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6243           foffset = offset;
6244         }
6245         break;
6246       default:
6247         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6248       }
6249     }
6250     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6251     /* Check permutation */
6252     {
6253       PetscInt *check;
6254 
6255       PetscCall(PetscMalloc1(size, &check));
6256       for (i = 0; i < size; ++i) {
6257         check[i] = -1;
6258         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6259       }
6260       for (i = 0; i < size; ++i) check[perm[i]] = i;
6261       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6262       PetscCall(PetscFree(check));
6263     }
6264     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6265     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6266       PetscInt *loc_perm;
6267       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6268       for (PetscInt i = 0; i < size; i++) {
6269         loc_perm[i]        = perm[i];
6270         loc_perm[size + i] = size + perm[i];
6271       }
6272       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6273     }
6274   }
6275   PetscFunctionReturn(PETSC_SUCCESS);
6276 }
6277 
6278 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6279 {
6280   PetscDS  prob;
6281   PetscInt depth, Nf, h;
6282   DMLabel  label;
6283 
6284   PetscFunctionBeginHot;
6285   PetscCall(DMGetDS(dm, &prob));
6286   Nf      = prob->Nf;
6287   label   = dm->depthLabel;
6288   *dspace = NULL;
6289   if (field < Nf) {
6290     PetscObject disc = prob->disc[field];
6291 
6292     if (disc->classid == PETSCFE_CLASSID) {
6293       PetscDualSpace dsp;
6294 
6295       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6296       PetscCall(DMLabelGetNumValues(label, &depth));
6297       PetscCall(DMLabelGetValue(label, point, &h));
6298       h = depth - 1 - h;
6299       if (h) {
6300         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6301       } else {
6302         *dspace = dsp;
6303       }
6304     }
6305   }
6306   PetscFunctionReturn(PETSC_SUCCESS);
6307 }
6308 
6309 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6310 {
6311   PetscScalar       *array;
6312   const PetscScalar *vArray;
6313   const PetscInt    *cone, *coneO;
6314   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6315 
6316   PetscFunctionBeginHot;
6317   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6318   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6319   PetscCall(DMPlexGetCone(dm, point, &cone));
6320   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6321   if (!values || !*values) {
6322     if ((point >= pStart) && (point < pEnd)) {
6323       PetscInt dof;
6324 
6325       PetscCall(PetscSectionGetDof(section, point, &dof));
6326       size += dof;
6327     }
6328     for (p = 0; p < numPoints; ++p) {
6329       const PetscInt cp = cone[p];
6330       PetscInt       dof;
6331 
6332       if ((cp < pStart) || (cp >= pEnd)) continue;
6333       PetscCall(PetscSectionGetDof(section, cp, &dof));
6334       size += dof;
6335     }
6336     if (!values) {
6337       if (csize) *csize = size;
6338       PetscFunctionReturn(PETSC_SUCCESS);
6339     }
6340     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6341   } else {
6342     array = *values;
6343   }
6344   size = 0;
6345   PetscCall(VecGetArrayRead(v, &vArray));
6346   if ((point >= pStart) && (point < pEnd)) {
6347     PetscInt           dof, off, d;
6348     const PetscScalar *varr;
6349 
6350     PetscCall(PetscSectionGetDof(section, point, &dof));
6351     PetscCall(PetscSectionGetOffset(section, point, &off));
6352     varr = PetscSafePointerPlusOffset(vArray, off);
6353     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6354     size += dof;
6355   }
6356   for (p = 0; p < numPoints; ++p) {
6357     const PetscInt     cp = cone[p];
6358     PetscInt           o  = coneO[p];
6359     PetscInt           dof, off, d;
6360     const PetscScalar *varr;
6361 
6362     if ((cp < pStart) || (cp >= pEnd)) continue;
6363     PetscCall(PetscSectionGetDof(section, cp, &dof));
6364     PetscCall(PetscSectionGetOffset(section, cp, &off));
6365     varr = PetscSafePointerPlusOffset(vArray, off);
6366     if (o >= 0) {
6367       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6368     } else {
6369       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6370     }
6371     size += dof;
6372   }
6373   PetscCall(VecRestoreArrayRead(v, &vArray));
6374   if (!*values) {
6375     if (csize) *csize = size;
6376     *values = array;
6377   } else {
6378     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6379     *csize = size;
6380   }
6381   PetscFunctionReturn(PETSC_SUCCESS);
6382 }
6383 
6384 /* Compress out points not in the section */
6385 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6386 {
6387   const PetscInt np = *numPoints;
6388   PetscInt       pStart, pEnd, p, q;
6389 
6390   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6391   for (p = 0, q = 0; p < np; ++p) {
6392     const PetscInt r = points[p * 2];
6393     if ((r >= pStart) && (r < pEnd)) {
6394       points[q * 2]     = r;
6395       points[q * 2 + 1] = points[p * 2 + 1];
6396       ++q;
6397     }
6398   }
6399   *numPoints = q;
6400   return PETSC_SUCCESS;
6401 }
6402 
6403 /* Compressed closure does not apply closure permutation */
6404 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6405 {
6406   const PetscInt *cla = NULL;
6407   PetscInt        np, *pts = NULL;
6408 
6409   PetscFunctionBeginHot;
6410   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6411   if (!ornt && *clPoints) {
6412     PetscInt dof, off;
6413 
6414     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6415     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6416     PetscCall(ISGetIndices(*clPoints, &cla));
6417     np  = dof / 2;
6418     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6419   } else {
6420     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6421     PetscCall(CompressPoints_Private(section, &np, pts));
6422   }
6423   *numPoints = np;
6424   *points    = pts;
6425   *clp       = cla;
6426   PetscFunctionReturn(PETSC_SUCCESS);
6427 }
6428 
6429 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6430 {
6431   PetscFunctionBeginHot;
6432   if (!*clPoints) {
6433     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6434   } else {
6435     PetscCall(ISRestoreIndices(*clPoints, clp));
6436   }
6437   *numPoints = 0;
6438   *points    = NULL;
6439   *clSec     = NULL;
6440   *clPoints  = NULL;
6441   *clp       = NULL;
6442   PetscFunctionReturn(PETSC_SUCCESS);
6443 }
6444 
6445 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6446 {
6447   PetscInt            offset = 0, p;
6448   const PetscInt    **perms  = NULL;
6449   const PetscScalar **flips  = NULL;
6450 
6451   PetscFunctionBeginHot;
6452   *size = 0;
6453   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6454   for (p = 0; p < numPoints; p++) {
6455     const PetscInt     point = points[2 * p];
6456     const PetscInt    *perm  = perms ? perms[p] : NULL;
6457     const PetscScalar *flip  = flips ? flips[p] : NULL;
6458     PetscInt           dof, off, d;
6459     const PetscScalar *varr;
6460 
6461     PetscCall(PetscSectionGetDof(section, point, &dof));
6462     PetscCall(PetscSectionGetOffset(section, point, &off));
6463     varr = PetscSafePointerPlusOffset(vArray, off);
6464     if (clperm) {
6465       if (perm) {
6466         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6467       } else {
6468         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6469       }
6470       if (flip) {
6471         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6472       }
6473     } else {
6474       if (perm) {
6475         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6476       } else {
6477         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6478       }
6479       if (flip) {
6480         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6481       }
6482     }
6483     offset += dof;
6484   }
6485   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6486   *size = offset;
6487   PetscFunctionReturn(PETSC_SUCCESS);
6488 }
6489 
6490 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[])
6491 {
6492   PetscInt offset = 0, f;
6493 
6494   PetscFunctionBeginHot;
6495   *size = 0;
6496   for (f = 0; f < numFields; ++f) {
6497     PetscInt            p;
6498     const PetscInt    **perms = NULL;
6499     const PetscScalar **flips = NULL;
6500 
6501     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6502     for (p = 0; p < numPoints; p++) {
6503       const PetscInt     point = points[2 * p];
6504       PetscInt           fdof, foff, b;
6505       const PetscScalar *varr;
6506       const PetscInt    *perm = perms ? perms[p] : NULL;
6507       const PetscScalar *flip = flips ? flips[p] : NULL;
6508 
6509       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6510       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6511       varr = &vArray[foff];
6512       if (clperm) {
6513         if (perm) {
6514           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6515         } else {
6516           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6517         }
6518         if (flip) {
6519           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6520         }
6521       } else {
6522         if (perm) {
6523           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6524         } else {
6525           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6526         }
6527         if (flip) {
6528           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6529         }
6530       }
6531       offset += fdof;
6532     }
6533     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6534   }
6535   *size = offset;
6536   PetscFunctionReturn(PETSC_SUCCESS);
6537 }
6538 
6539 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6540 {
6541   PetscSection    clSection;
6542   IS              clPoints;
6543   PetscInt       *points = NULL;
6544   const PetscInt *clp, *perm = NULL;
6545   PetscInt        depth, numFields, numPoints, asize;
6546 
6547   PetscFunctionBeginHot;
6548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6549   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6550   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6551   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6552   PetscCall(DMPlexGetDepth(dm, &depth));
6553   PetscCall(PetscSectionGetNumFields(section, &numFields));
6554   if (depth == 1 && numFields < 2) {
6555     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6556     PetscFunctionReturn(PETSC_SUCCESS);
6557   }
6558   /* Get points */
6559   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6560   /* Get sizes */
6561   asize = 0;
6562   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6563     PetscInt dof;
6564     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6565     asize += dof;
6566   }
6567   if (values) {
6568     const PetscScalar *vArray;
6569     PetscInt           size;
6570 
6571     if (*values) {
6572       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);
6573     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6574     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6575     PetscCall(VecGetArrayRead(v, &vArray));
6576     /* Get values */
6577     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6578     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6579     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6580     /* Cleanup array */
6581     PetscCall(VecRestoreArrayRead(v, &vArray));
6582   }
6583   if (csize) *csize = asize;
6584   /* Cleanup points */
6585   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6586   PetscFunctionReturn(PETSC_SUCCESS);
6587 }
6588 
6589 /*@C
6590   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6591 
6592   Not collective
6593 
6594   Input Parameters:
6595 + dm      - The `DM`
6596 . section - The section describing the layout in `v`, or `NULL` to use the default section
6597 . v       - The local vector
6598 - point   - The point in the `DM`
6599 
6600   Input/Output Parameters:
6601 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6602 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6603            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6604 
6605   Level: intermediate
6606 
6607   Notes:
6608   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6609   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6610   assembly function, and a user may already have allocated storage for this operation.
6611 
6612   A typical use could be
6613 .vb
6614    values = NULL;
6615    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6616    for (cl = 0; cl < clSize; ++cl) {
6617      <Compute on closure>
6618    }
6619    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6620 .ve
6621   or
6622 .vb
6623    PetscMalloc1(clMaxSize, &values);
6624    for (p = pStart; p < pEnd; ++p) {
6625      clSize = clMaxSize;
6626      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6627      for (cl = 0; cl < clSize; ++cl) {
6628        <Compute on closure>
6629      }
6630    }
6631    PetscFree(values);
6632 .ve
6633 
6634   Fortran Notes:
6635   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6636   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6637 
6638   `values` must be declared with
6639 .vb
6640   PetscScalar,dimension(:),pointer   :: values
6641 .ve
6642   and it will be allocated internally by PETSc to hold the values returned
6643 
6644 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6645 @*/
6646 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6647 {
6648   PetscFunctionBeginHot;
6649   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6650   PetscFunctionReturn(PETSC_SUCCESS);
6651 }
6652 
6653 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6654 {
6655   DMLabel            depthLabel;
6656   PetscSection       clSection;
6657   IS                 clPoints;
6658   PetscScalar       *array;
6659   const PetscScalar *vArray;
6660   PetscInt          *points = NULL;
6661   const PetscInt    *clp, *perm = NULL;
6662   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6663 
6664   PetscFunctionBeginHot;
6665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6666   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6667   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6668   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6669   PetscCall(DMPlexGetDepth(dm, &mdepth));
6670   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6671   PetscCall(PetscSectionGetNumFields(section, &numFields));
6672   if (mdepth == 1 && numFields < 2) {
6673     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6674     PetscFunctionReturn(PETSC_SUCCESS);
6675   }
6676   /* Get points */
6677   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6678   for (clsize = 0, p = 0; p < Np; p++) {
6679     PetscInt dof;
6680     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6681     clsize += dof;
6682   }
6683   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6684   /* Filter points */
6685   for (p = 0; p < numPoints * 2; p += 2) {
6686     PetscInt dep;
6687 
6688     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6689     if (dep != depth) continue;
6690     points[Np * 2 + 0] = points[p];
6691     points[Np * 2 + 1] = points[p + 1];
6692     ++Np;
6693   }
6694   /* Get array */
6695   if (!values || !*values) {
6696     PetscInt asize = 0, dof;
6697 
6698     for (p = 0; p < Np * 2; p += 2) {
6699       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6700       asize += dof;
6701     }
6702     if (!values) {
6703       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6704       if (csize) *csize = asize;
6705       PetscFunctionReturn(PETSC_SUCCESS);
6706     }
6707     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6708   } else {
6709     array = *values;
6710   }
6711   PetscCall(VecGetArrayRead(v, &vArray));
6712   /* Get values */
6713   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6714   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6715   /* Cleanup points */
6716   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6717   /* Cleanup array */
6718   PetscCall(VecRestoreArrayRead(v, &vArray));
6719   if (!*values) {
6720     if (csize) *csize = size;
6721     *values = array;
6722   } else {
6723     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6724     *csize = size;
6725   }
6726   PetscFunctionReturn(PETSC_SUCCESS);
6727 }
6728 
6729 /*@C
6730   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6731 
6732   Not collective
6733 
6734   Input Parameters:
6735 + dm      - The `DM`
6736 . section - The section describing the layout in `v`, or `NULL` to use the default section
6737 . v       - The local vector
6738 . point   - The point in the `DM`
6739 . csize   - The number of values in the closure, or `NULL`
6740 - values  - The array of values
6741 
6742   Level: intermediate
6743 
6744   Note:
6745   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6746 
6747   Fortran Note:
6748   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6749   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6750 
6751 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6752 @*/
6753 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6754 {
6755   PetscInt size = 0;
6756 
6757   PetscFunctionBegin;
6758   /* Should work without recalculating size */
6759   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6760   *values = NULL;
6761   PetscFunctionReturn(PETSC_SUCCESS);
6762 }
6763 
6764 static inline void add(PetscScalar *x, PetscScalar y)
6765 {
6766   *x += y;
6767 }
6768 static inline void insert(PetscScalar *x, PetscScalar y)
6769 {
6770   *x = y;
6771 }
6772 
6773 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[])
6774 {
6775   PetscInt        cdof;  /* The number of constraints on this point */
6776   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6777   PetscScalar    *a;
6778   PetscInt        off, cind = 0, k;
6779 
6780   PetscFunctionBegin;
6781   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6782   PetscCall(PetscSectionGetOffset(section, point, &off));
6783   a = &array[off];
6784   if (!cdof || setBC) {
6785     if (clperm) {
6786       if (perm) {
6787         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6788       } else {
6789         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6790       }
6791     } else {
6792       if (perm) {
6793         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6794       } else {
6795         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6796       }
6797     }
6798   } else {
6799     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6800     if (clperm) {
6801       if (perm) {
6802         for (k = 0; k < dof; ++k) {
6803           if ((cind < cdof) && (k == cdofs[cind])) {
6804             ++cind;
6805             continue;
6806           }
6807           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6808         }
6809       } else {
6810         for (k = 0; k < dof; ++k) {
6811           if ((cind < cdof) && (k == cdofs[cind])) {
6812             ++cind;
6813             continue;
6814           }
6815           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6816         }
6817       }
6818     } else {
6819       if (perm) {
6820         for (k = 0; k < dof; ++k) {
6821           if ((cind < cdof) && (k == cdofs[cind])) {
6822             ++cind;
6823             continue;
6824           }
6825           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6826         }
6827       } else {
6828         for (k = 0; k < dof; ++k) {
6829           if ((cind < cdof) && (k == cdofs[cind])) {
6830             ++cind;
6831             continue;
6832           }
6833           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6834         }
6835       }
6836     }
6837   }
6838   PetscFunctionReturn(PETSC_SUCCESS);
6839 }
6840 
6841 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[])
6842 {
6843   PetscInt        cdof;  /* The number of constraints on this point */
6844   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6845   PetscScalar    *a;
6846   PetscInt        off, cind = 0, k;
6847 
6848   PetscFunctionBegin;
6849   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6850   PetscCall(PetscSectionGetOffset(section, point, &off));
6851   a = &array[off];
6852   if (cdof) {
6853     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6854     if (clperm) {
6855       if (perm) {
6856         for (k = 0; k < dof; ++k) {
6857           if ((cind < cdof) && (k == cdofs[cind])) {
6858             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6859             cind++;
6860           }
6861         }
6862       } else {
6863         for (k = 0; k < dof; ++k) {
6864           if ((cind < cdof) && (k == cdofs[cind])) {
6865             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6866             cind++;
6867           }
6868         }
6869       }
6870     } else {
6871       if (perm) {
6872         for (k = 0; k < dof; ++k) {
6873           if ((cind < cdof) && (k == cdofs[cind])) {
6874             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6875             cind++;
6876           }
6877         }
6878       } else {
6879         for (k = 0; k < dof; ++k) {
6880           if ((cind < cdof) && (k == cdofs[cind])) {
6881             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6882             cind++;
6883           }
6884         }
6885       }
6886     }
6887   }
6888   PetscFunctionReturn(PETSC_SUCCESS);
6889 }
6890 
6891 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[])
6892 {
6893   PetscScalar    *a;
6894   PetscInt        fdof, foff, fcdof, foffset = *offset;
6895   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6896   PetscInt        cind = 0, b;
6897 
6898   PetscFunctionBegin;
6899   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6900   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6901   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6902   a = &array[foff];
6903   if (!fcdof || setBC) {
6904     if (clperm) {
6905       if (perm) {
6906         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6907       } else {
6908         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6909       }
6910     } else {
6911       if (perm) {
6912         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6913       } else {
6914         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6915       }
6916     }
6917   } else {
6918     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6919     if (clperm) {
6920       if (perm) {
6921         for (b = 0; b < fdof; b++) {
6922           if ((cind < fcdof) && (b == fcdofs[cind])) {
6923             ++cind;
6924             continue;
6925           }
6926           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6927         }
6928       } else {
6929         for (b = 0; b < fdof; b++) {
6930           if ((cind < fcdof) && (b == fcdofs[cind])) {
6931             ++cind;
6932             continue;
6933           }
6934           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6935         }
6936       }
6937     } else {
6938       if (perm) {
6939         for (b = 0; b < fdof; b++) {
6940           if ((cind < fcdof) && (b == fcdofs[cind])) {
6941             ++cind;
6942             continue;
6943           }
6944           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6945         }
6946       } else {
6947         for (b = 0; b < fdof; b++) {
6948           if ((cind < fcdof) && (b == fcdofs[cind])) {
6949             ++cind;
6950             continue;
6951           }
6952           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6953         }
6954       }
6955     }
6956   }
6957   *offset += fdof;
6958   PetscFunctionReturn(PETSC_SUCCESS);
6959 }
6960 
6961 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[])
6962 {
6963   PetscScalar    *a;
6964   PetscInt        fdof, foff, fcdof, foffset = *offset;
6965   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6966   PetscInt        Nc, cind = 0, ncind = 0, b;
6967   PetscBool       ncSet, fcSet;
6968 
6969   PetscFunctionBegin;
6970   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6971   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6972   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6973   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6974   a = &array[foff];
6975   if (fcdof) {
6976     /* We just override fcdof and fcdofs with Ncc and comps */
6977     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6978     if (clperm) {
6979       if (perm) {
6980         if (comps) {
6981           for (b = 0; b < fdof; b++) {
6982             ncSet = fcSet = PETSC_FALSE;
6983             if (b % Nc == comps[ncind]) {
6984               ncind = (ncind + 1) % Ncc;
6985               ncSet = PETSC_TRUE;
6986             }
6987             if ((cind < fcdof) && (b == fcdofs[cind])) {
6988               ++cind;
6989               fcSet = PETSC_TRUE;
6990             }
6991             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6992           }
6993         } else {
6994           for (b = 0; b < fdof; b++) {
6995             if ((cind < fcdof) && (b == fcdofs[cind])) {
6996               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6997               ++cind;
6998             }
6999           }
7000         }
7001       } else {
7002         if (comps) {
7003           for (b = 0; b < fdof; b++) {
7004             ncSet = fcSet = PETSC_FALSE;
7005             if (b % Nc == comps[ncind]) {
7006               ncind = (ncind + 1) % Ncc;
7007               ncSet = PETSC_TRUE;
7008             }
7009             if ((cind < fcdof) && (b == fcdofs[cind])) {
7010               ++cind;
7011               fcSet = PETSC_TRUE;
7012             }
7013             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7014           }
7015         } else {
7016           for (b = 0; b < fdof; b++) {
7017             if ((cind < fcdof) && (b == fcdofs[cind])) {
7018               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7019               ++cind;
7020             }
7021           }
7022         }
7023       }
7024     } else {
7025       if (perm) {
7026         if (comps) {
7027           for (b = 0; b < fdof; b++) {
7028             ncSet = fcSet = PETSC_FALSE;
7029             if (b % Nc == comps[ncind]) {
7030               ncind = (ncind + 1) % Ncc;
7031               ncSet = PETSC_TRUE;
7032             }
7033             if ((cind < fcdof) && (b == fcdofs[cind])) {
7034               ++cind;
7035               fcSet = PETSC_TRUE;
7036             }
7037             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7038           }
7039         } else {
7040           for (b = 0; b < fdof; b++) {
7041             if ((cind < fcdof) && (b == fcdofs[cind])) {
7042               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7043               ++cind;
7044             }
7045           }
7046         }
7047       } else {
7048         if (comps) {
7049           for (b = 0; b < fdof; b++) {
7050             ncSet = fcSet = PETSC_FALSE;
7051             if (b % Nc == comps[ncind]) {
7052               ncind = (ncind + 1) % Ncc;
7053               ncSet = PETSC_TRUE;
7054             }
7055             if ((cind < fcdof) && (b == fcdofs[cind])) {
7056               ++cind;
7057               fcSet = PETSC_TRUE;
7058             }
7059             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7060           }
7061         } else {
7062           for (b = 0; b < fdof; b++) {
7063             if ((cind < fcdof) && (b == fcdofs[cind])) {
7064               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7065               ++cind;
7066             }
7067           }
7068         }
7069       }
7070     }
7071   }
7072   *offset += fdof;
7073   PetscFunctionReturn(PETSC_SUCCESS);
7074 }
7075 
7076 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7077 {
7078   PetscScalar    *array;
7079   const PetscInt *cone, *coneO;
7080   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7081 
7082   PetscFunctionBeginHot;
7083   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7084   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7085   PetscCall(DMPlexGetCone(dm, point, &cone));
7086   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7087   PetscCall(VecGetArray(v, &array));
7088   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7089     const PetscInt cp = !p ? point : cone[p - 1];
7090     const PetscInt o  = !p ? 0 : coneO[p - 1];
7091 
7092     if ((cp < pStart) || (cp >= pEnd)) {
7093       dof = 0;
7094       continue;
7095     }
7096     PetscCall(PetscSectionGetDof(section, cp, &dof));
7097     /* ADD_VALUES */
7098     {
7099       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7100       PetscScalar    *a;
7101       PetscInt        cdof, coff, cind = 0, k;
7102 
7103       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7104       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7105       a = &array[coff];
7106       if (!cdof) {
7107         if (o >= 0) {
7108           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7109         } else {
7110           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7111         }
7112       } else {
7113         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7114         if (o >= 0) {
7115           for (k = 0; k < dof; ++k) {
7116             if ((cind < cdof) && (k == cdofs[cind])) {
7117               ++cind;
7118               continue;
7119             }
7120             a[k] += values[off + k];
7121           }
7122         } else {
7123           for (k = 0; k < dof; ++k) {
7124             if ((cind < cdof) && (k == cdofs[cind])) {
7125               ++cind;
7126               continue;
7127             }
7128             a[k] += values[off + dof - k - 1];
7129           }
7130         }
7131       }
7132     }
7133   }
7134   PetscCall(VecRestoreArray(v, &array));
7135   PetscFunctionReturn(PETSC_SUCCESS);
7136 }
7137 
7138 /*@C
7139   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7140 
7141   Not collective
7142 
7143   Input Parameters:
7144 + dm      - The `DM`
7145 . section - The section describing the layout in `v`, or `NULL` to use the default section
7146 . v       - The local vector
7147 . point   - The point in the `DM`
7148 . values  - The array of values
7149 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7150             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7151 
7152   Level: intermediate
7153 
7154   Note:
7155   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7156 
7157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7158 @*/
7159 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7160 {
7161   PetscSection    clSection;
7162   IS              clPoints;
7163   PetscScalar    *array;
7164   PetscInt       *points = NULL;
7165   const PetscInt *clp, *clperm = NULL;
7166   PetscInt        depth, numFields, numPoints, p, clsize;
7167 
7168   PetscFunctionBeginHot;
7169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7170   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7171   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7172   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7173   PetscCall(DMPlexGetDepth(dm, &depth));
7174   PetscCall(PetscSectionGetNumFields(section, &numFields));
7175   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7176     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7177     PetscFunctionReturn(PETSC_SUCCESS);
7178   }
7179   /* Get points */
7180   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7181   for (clsize = 0, p = 0; p < numPoints; p++) {
7182     PetscInt dof;
7183     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7184     clsize += dof;
7185   }
7186   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7187   /* Get array */
7188   PetscCall(VecGetArray(v, &array));
7189   /* Get values */
7190   if (numFields > 0) {
7191     PetscInt offset = 0, f;
7192     for (f = 0; f < numFields; ++f) {
7193       const PetscInt    **perms = NULL;
7194       const PetscScalar **flips = NULL;
7195 
7196       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7197       switch (mode) {
7198       case INSERT_VALUES:
7199         for (p = 0; p < numPoints; p++) {
7200           const PetscInt     point = points[2 * p];
7201           const PetscInt    *perm  = perms ? perms[p] : NULL;
7202           const PetscScalar *flip  = flips ? flips[p] : NULL;
7203           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7204         }
7205         break;
7206       case INSERT_ALL_VALUES:
7207         for (p = 0; p < numPoints; p++) {
7208           const PetscInt     point = points[2 * p];
7209           const PetscInt    *perm  = perms ? perms[p] : NULL;
7210           const PetscScalar *flip  = flips ? flips[p] : NULL;
7211           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7212         }
7213         break;
7214       case INSERT_BC_VALUES:
7215         for (p = 0; p < numPoints; p++) {
7216           const PetscInt     point = points[2 * p];
7217           const PetscInt    *perm  = perms ? perms[p] : NULL;
7218           const PetscScalar *flip  = flips ? flips[p] : NULL;
7219           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7220         }
7221         break;
7222       case ADD_VALUES:
7223         for (p = 0; p < numPoints; p++) {
7224           const PetscInt     point = points[2 * p];
7225           const PetscInt    *perm  = perms ? perms[p] : NULL;
7226           const PetscScalar *flip  = flips ? flips[p] : NULL;
7227           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7228         }
7229         break;
7230       case ADD_ALL_VALUES:
7231         for (p = 0; p < numPoints; p++) {
7232           const PetscInt     point = points[2 * p];
7233           const PetscInt    *perm  = perms ? perms[p] : NULL;
7234           const PetscScalar *flip  = flips ? flips[p] : NULL;
7235           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7236         }
7237         break;
7238       case ADD_BC_VALUES:
7239         for (p = 0; p < numPoints; p++) {
7240           const PetscInt     point = points[2 * p];
7241           const PetscInt    *perm  = perms ? perms[p] : NULL;
7242           const PetscScalar *flip  = flips ? flips[p] : NULL;
7243           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7244         }
7245         break;
7246       default:
7247         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7248       }
7249       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7250     }
7251   } else {
7252     PetscInt            dof, off;
7253     const PetscInt    **perms = NULL;
7254     const PetscScalar **flips = NULL;
7255 
7256     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7257     switch (mode) {
7258     case INSERT_VALUES:
7259       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7260         const PetscInt     point = points[2 * p];
7261         const PetscInt    *perm  = perms ? perms[p] : NULL;
7262         const PetscScalar *flip  = flips ? flips[p] : NULL;
7263         PetscCall(PetscSectionGetDof(section, point, &dof));
7264         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7265       }
7266       break;
7267     case INSERT_ALL_VALUES:
7268       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7269         const PetscInt     point = points[2 * p];
7270         const PetscInt    *perm  = perms ? perms[p] : NULL;
7271         const PetscScalar *flip  = flips ? flips[p] : NULL;
7272         PetscCall(PetscSectionGetDof(section, point, &dof));
7273         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7274       }
7275       break;
7276     case INSERT_BC_VALUES:
7277       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7278         const PetscInt     point = points[2 * p];
7279         const PetscInt    *perm  = perms ? perms[p] : NULL;
7280         const PetscScalar *flip  = flips ? flips[p] : NULL;
7281         PetscCall(PetscSectionGetDof(section, point, &dof));
7282         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7283       }
7284       break;
7285     case ADD_VALUES:
7286       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7287         const PetscInt     point = points[2 * p];
7288         const PetscInt    *perm  = perms ? perms[p] : NULL;
7289         const PetscScalar *flip  = flips ? flips[p] : NULL;
7290         PetscCall(PetscSectionGetDof(section, point, &dof));
7291         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7292       }
7293       break;
7294     case ADD_ALL_VALUES:
7295       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7296         const PetscInt     point = points[2 * p];
7297         const PetscInt    *perm  = perms ? perms[p] : NULL;
7298         const PetscScalar *flip  = flips ? flips[p] : NULL;
7299         PetscCall(PetscSectionGetDof(section, point, &dof));
7300         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7301       }
7302       break;
7303     case ADD_BC_VALUES:
7304       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7305         const PetscInt     point = points[2 * p];
7306         const PetscInt    *perm  = perms ? perms[p] : NULL;
7307         const PetscScalar *flip  = flips ? flips[p] : NULL;
7308         PetscCall(PetscSectionGetDof(section, point, &dof));
7309         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7310       }
7311       break;
7312     default:
7313       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7314     }
7315     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7316   }
7317   /* Cleanup points */
7318   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7319   /* Cleanup array */
7320   PetscCall(VecRestoreArray(v, &array));
7321   PetscFunctionReturn(PETSC_SUCCESS);
7322 }
7323 
7324 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7325 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7326 {
7327   PetscFunctionBegin;
7328   *contains = PETSC_TRUE;
7329   if (label) {
7330     PetscInt fdof;
7331 
7332     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7333     if (!*contains) {
7334       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7335       *offset += fdof;
7336       PetscFunctionReturn(PETSC_SUCCESS);
7337     }
7338   }
7339   PetscFunctionReturn(PETSC_SUCCESS);
7340 }
7341 
7342 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7343 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)
7344 {
7345   PetscSection    clSection;
7346   IS              clPoints;
7347   PetscScalar    *array;
7348   PetscInt       *points = NULL;
7349   const PetscInt *clp;
7350   PetscInt        numFields, numPoints, p;
7351   PetscInt        offset = 0, f;
7352 
7353   PetscFunctionBeginHot;
7354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7355   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7356   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7357   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7358   PetscCall(PetscSectionGetNumFields(section, &numFields));
7359   /* Get points */
7360   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7361   /* Get array */
7362   PetscCall(VecGetArray(v, &array));
7363   /* Get values */
7364   for (f = 0; f < numFields; ++f) {
7365     const PetscInt    **perms = NULL;
7366     const PetscScalar **flips = NULL;
7367     PetscBool           contains;
7368 
7369     if (!fieldActive[f]) {
7370       for (p = 0; p < numPoints * 2; p += 2) {
7371         PetscInt fdof;
7372         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7373         offset += fdof;
7374       }
7375       continue;
7376     }
7377     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7378     switch (mode) {
7379     case INSERT_VALUES:
7380       for (p = 0; p < numPoints; p++) {
7381         const PetscInt     point = points[2 * p];
7382         const PetscInt    *perm  = perms ? perms[p] : NULL;
7383         const PetscScalar *flip  = flips ? flips[p] : NULL;
7384         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7385         if (!contains) continue;
7386         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7387       }
7388       break;
7389     case INSERT_ALL_VALUES:
7390       for (p = 0; p < numPoints; p++) {
7391         const PetscInt     point = points[2 * p];
7392         const PetscInt    *perm  = perms ? perms[p] : NULL;
7393         const PetscScalar *flip  = flips ? flips[p] : NULL;
7394         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7395         if (!contains) continue;
7396         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7397       }
7398       break;
7399     case INSERT_BC_VALUES:
7400       for (p = 0; p < numPoints; p++) {
7401         const PetscInt     point = points[2 * p];
7402         const PetscInt    *perm  = perms ? perms[p] : NULL;
7403         const PetscScalar *flip  = flips ? flips[p] : NULL;
7404         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7405         if (!contains) continue;
7406         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7407       }
7408       break;
7409     case ADD_VALUES:
7410       for (p = 0; p < numPoints; p++) {
7411         const PetscInt     point = points[2 * p];
7412         const PetscInt    *perm  = perms ? perms[p] : NULL;
7413         const PetscScalar *flip  = flips ? flips[p] : NULL;
7414         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7415         if (!contains) continue;
7416         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7417       }
7418       break;
7419     case ADD_ALL_VALUES:
7420       for (p = 0; p < numPoints; p++) {
7421         const PetscInt     point = points[2 * p];
7422         const PetscInt    *perm  = perms ? perms[p] : NULL;
7423         const PetscScalar *flip  = flips ? flips[p] : NULL;
7424         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7425         if (!contains) continue;
7426         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7427       }
7428       break;
7429     default:
7430       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7431     }
7432     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7433   }
7434   /* Cleanup points */
7435   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7436   /* Cleanup array */
7437   PetscCall(VecRestoreArray(v, &array));
7438   PetscFunctionReturn(PETSC_SUCCESS);
7439 }
7440 
7441 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7442 {
7443   PetscMPIInt rank;
7444   PetscInt    i, j;
7445 
7446   PetscFunctionBegin;
7447   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7448   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7449   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7450   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7451   numCIndices = numCIndices ? numCIndices : numRIndices;
7452   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7453   for (i = 0; i < numRIndices; i++) {
7454     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7455     for (j = 0; j < numCIndices; j++) {
7456 #if defined(PETSC_USE_COMPLEX)
7457       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7458 #else
7459       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7460 #endif
7461     }
7462     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7463   }
7464   PetscFunctionReturn(PETSC_SUCCESS);
7465 }
7466 
7467 /*
7468   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7469 
7470   Input Parameters:
7471 + section - The section for this data layout
7472 . islocal - Is the section (and thus indices being requested) local or global?
7473 . point   - The point contributing dofs with these indices
7474 . off     - The global offset of this point
7475 . loff    - The local offset of each field
7476 . setBC   - The flag determining whether to include indices of boundary values
7477 . perm    - A permutation of the dofs on this point, or NULL
7478 - indperm - A permutation of the entire indices array, or NULL
7479 
7480   Output Parameter:
7481 . indices - Indices for dofs on this point
7482 
7483   Level: developer
7484 
7485   Note: The indices could be local or global, depending on the value of 'off'.
7486 */
7487 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7488 {
7489   PetscInt        dof;   /* The number of unknowns on this point */
7490   PetscInt        cdof;  /* The number of constraints on this point */
7491   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7492   PetscInt        cind = 0, k;
7493 
7494   PetscFunctionBegin;
7495   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7496   PetscCall(PetscSectionGetDof(section, point, &dof));
7497   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7498   if (!cdof || setBC) {
7499     for (k = 0; k < dof; ++k) {
7500       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7501       const PetscInt ind    = indperm ? indperm[preind] : preind;
7502 
7503       indices[ind] = off + k;
7504     }
7505   } else {
7506     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7507     for (k = 0; k < dof; ++k) {
7508       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7509       const PetscInt ind    = indperm ? indperm[preind] : preind;
7510 
7511       if ((cind < cdof) && (k == cdofs[cind])) {
7512         /* Insert check for returning constrained indices */
7513         indices[ind] = -(off + k + 1);
7514         ++cind;
7515       } else {
7516         indices[ind] = off + k - (islocal ? 0 : cind);
7517       }
7518     }
7519   }
7520   *loff += dof;
7521   PetscFunctionReturn(PETSC_SUCCESS);
7522 }
7523 
7524 /*
7525  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7526 
7527  Input Parameters:
7528 + section - a section (global or local)
7529 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7530 . point - point within section
7531 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7532 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7533 . setBC - identify constrained (boundary condition) points via involution.
7534 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7535 . permsoff - offset
7536 - indperm - index permutation
7537 
7538  Output Parameter:
7539 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7540 . indices - array to hold indices (as defined by section) of each dof associated with point
7541 
7542  Notes:
7543  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7544  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7545  in the local vector.
7546 
7547  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7548  significant).  It is invalid to call with a global section and setBC=true.
7549 
7550  Developer Note:
7551  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7552  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7553  offset could be obtained from the section instead of passing it explicitly as we do now.
7554 
7555  Example:
7556  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7557  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7558  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7559  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.
7560 
7561  Level: developer
7562 */
7563 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[])
7564 {
7565   PetscInt numFields, foff, f;
7566 
7567   PetscFunctionBegin;
7568   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7569   PetscCall(PetscSectionGetNumFields(section, &numFields));
7570   for (f = 0, foff = 0; f < numFields; ++f) {
7571     PetscInt        fdof, cfdof;
7572     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7573     PetscInt        cind = 0, b;
7574     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7575 
7576     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7577     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7578     if (!cfdof || setBC) {
7579       for (b = 0; b < fdof; ++b) {
7580         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7581         const PetscInt ind    = indperm ? indperm[preind] : preind;
7582 
7583         indices[ind] = off + foff + b;
7584       }
7585     } else {
7586       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7587       for (b = 0; b < fdof; ++b) {
7588         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7589         const PetscInt ind    = indperm ? indperm[preind] : preind;
7590 
7591         if ((cind < cfdof) && (b == fcdofs[cind])) {
7592           indices[ind] = -(off + foff + b + 1);
7593           ++cind;
7594         } else {
7595           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7596         }
7597       }
7598     }
7599     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7600     foffs[f] += fdof;
7601   }
7602   PetscFunctionReturn(PETSC_SUCCESS);
7603 }
7604 
7605 /*
7606   This version believes the globalSection offsets for each field, rather than just the point offset
7607 
7608  . foffs - The offset into 'indices' for each field, since it is segregated by field
7609 
7610  Notes:
7611  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7612  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7613 */
7614 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7615 {
7616   PetscInt numFields, foff, f;
7617 
7618   PetscFunctionBegin;
7619   PetscCall(PetscSectionGetNumFields(section, &numFields));
7620   for (f = 0; f < numFields; ++f) {
7621     PetscInt        fdof, cfdof;
7622     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7623     PetscInt        cind = 0, b;
7624     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7625 
7626     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7627     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7628     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7629     if (!cfdof) {
7630       for (b = 0; b < fdof; ++b) {
7631         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7632         const PetscInt ind    = indperm ? indperm[preind] : preind;
7633 
7634         indices[ind] = foff + b;
7635       }
7636     } else {
7637       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7638       for (b = 0; b < fdof; ++b) {
7639         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7640         const PetscInt ind    = indperm ? indperm[preind] : preind;
7641 
7642         if ((cind < cfdof) && (b == fcdofs[cind])) {
7643           indices[ind] = -(foff + b + 1);
7644           ++cind;
7645         } else {
7646           indices[ind] = foff + b - cind;
7647         }
7648       }
7649     }
7650     foffs[f] += fdof;
7651   }
7652   PetscFunctionReturn(PETSC_SUCCESS);
7653 }
7654 
7655 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7656 {
7657   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7658 
7659   PetscFunctionBegin;
7660   PetscCall(PetscSectionGetNumFields(section, &numFields));
7661   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7662   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7663   for (PetscInt p = 0; p < nPoints; p++) {
7664     PetscInt     b       = pnts[2 * p];
7665     PetscInt     bSecDof = 0, bOff;
7666     PetscInt     cSecDof = 0;
7667     PetscSection indices_section;
7668 
7669     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7670     if (!bSecDof) continue;
7671     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7672     indices_section = cSecDof > 0 ? cSec : section;
7673     if (numFields) {
7674       PetscInt fStart[32], fEnd[32];
7675 
7676       fStart[0] = 0;
7677       fEnd[0]   = 0;
7678       for (PetscInt f = 0; f < numFields; f++) {
7679         PetscInt fDof = 0;
7680 
7681         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7682         fStart[f + 1] = fStart[f] + fDof;
7683         fEnd[f + 1]   = fStart[f + 1];
7684       }
7685       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7686       // only apply permutations on one side
7687       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7688       for (PetscInt f = 0; f < numFields; f++) {
7689         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7690       }
7691     } else {
7692       PetscInt bEnd = 0;
7693 
7694       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7695       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7696 
7697       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7698     }
7699   }
7700   PetscFunctionReturn(PETSC_SUCCESS);
7701 }
7702 
7703 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[])
7704 {
7705   Mat             cMat;
7706   PetscSection    aSec, cSec;
7707   IS              aIS;
7708   PetscInt        aStart = -1, aEnd = -1;
7709   PetscInt        sStart = -1, sEnd = -1;
7710   PetscInt        cStart = -1, cEnd = -1;
7711   const PetscInt *anchors;
7712   PetscInt        numFields, p;
7713   PetscInt        newNumPoints = 0, newNumIndices = 0;
7714   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7715   PetscInt        oldOffsets[32];
7716   PetscInt        newOffsets[32];
7717   PetscInt        oldOffsetsCopy[32];
7718   PetscInt        newOffsetsCopy[32];
7719   PetscScalar    *modMat         = NULL;
7720   PetscBool       anyConstrained = PETSC_FALSE;
7721 
7722   PetscFunctionBegin;
7723   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7724   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7725   PetscCall(PetscSectionGetNumFields(section, &numFields));
7726 
7727   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7728   /* if there are point-to-point constraints */
7729   if (aSec) {
7730     PetscCall(PetscArrayzero(newOffsets, 32));
7731     PetscCall(PetscArrayzero(oldOffsets, 32));
7732     PetscCall(ISGetIndices(aIS, &anchors));
7733     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7734     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7735     /* figure out how many points are going to be in the new element matrix
7736      * (we allow double counting, because it's all just going to be summed
7737      * into the global matrix anyway) */
7738     for (p = 0; p < 2 * numPoints; p += 2) {
7739       PetscInt b    = points[p];
7740       PetscInt bDof = 0, bSecDof = 0;
7741 
7742       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7743       if (!bSecDof) continue;
7744 
7745       for (PetscInt f = 0; f < numFields; f++) {
7746         PetscInt fDof = 0;
7747 
7748         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7749         oldOffsets[f + 1] += fDof;
7750       }
7751       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7752       if (bDof) {
7753         /* this point is constrained */
7754         /* it is going to be replaced by its anchors */
7755         PetscInt bOff, q;
7756 
7757         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7758         for (q = 0; q < bDof; q++) {
7759           PetscInt a    = anchors[bOff + q];
7760           PetscInt aDof = 0;
7761 
7762           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7763           if (aDof) {
7764             anyConstrained = PETSC_TRUE;
7765             newNumPoints += 1;
7766           }
7767           newNumIndices += aDof;
7768           for (PetscInt f = 0; f < numFields; ++f) {
7769             PetscInt fDof = 0;
7770 
7771             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7772             newOffsets[f + 1] += fDof;
7773           }
7774         }
7775       } else {
7776         /* this point is not constrained */
7777         newNumPoints++;
7778         newNumIndices += bSecDof;
7779         for (PetscInt f = 0; f < numFields; ++f) {
7780           PetscInt fDof;
7781 
7782           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7783           newOffsets[f + 1] += fDof;
7784         }
7785       }
7786     }
7787   }
7788   if (!anyConstrained) {
7789     if (outNumPoints) *outNumPoints = 0;
7790     if (outNumIndices) *outNumIndices = 0;
7791     if (outPoints) *outPoints = NULL;
7792     if (outMat) *outMat = NULL;
7793     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7794     PetscFunctionReturn(PETSC_SUCCESS);
7795   }
7796 
7797   if (outNumPoints) *outNumPoints = newNumPoints;
7798   if (outNumIndices) *outNumIndices = newNumIndices;
7799 
7800   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7801   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7802 
7803   if (!outPoints && !outMat) {
7804     if (offsets) {
7805       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7806     }
7807     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7808     PetscFunctionReturn(PETSC_SUCCESS);
7809   }
7810 
7811   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7812   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7813 
7814   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7815   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7816 
7817   /* output arrays */
7818   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7819   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7820 
7821   // get the new Points
7822   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7823     PetscInt b    = points[2 * p];
7824     PetscInt bDof = 0, bSecDof = 0, bOff;
7825 
7826     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7827     if (!bSecDof) continue;
7828     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7829     if (bDof) {
7830       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7831       for (PetscInt q = 0; q < bDof; q++) {
7832         PetscInt a = anchors[bOff + q], aDof = 0;
7833 
7834         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7835         if (aDof) {
7836           newPoints[2 * newP]     = a;
7837           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7838           newP++;
7839         }
7840       }
7841     } else {
7842       newPoints[2 * newP]     = b;
7843       newPoints[2 * newP + 1] = points[2 * p + 1];
7844       newP++;
7845     }
7846   }
7847 
7848   if (outMat) {
7849     PetscScalar *tmpMat;
7850     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7851     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7852 
7853     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7854     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7855     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7856     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7857 
7858     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7859     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7860 
7861     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7862     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7863 
7864     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7865     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7866     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7867     // for each field, insert the anchor modification into modMat
7868     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7869       PetscInt fStart    = oldOffsets[f];
7870       PetscInt fNewStart = newOffsets[f];
7871       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7872         PetscInt b    = points[2 * p];
7873         PetscInt bDof = 0, bSecDof = 0, bOff;
7874 
7875         if (b >= sStart && b < sEnd) {
7876           if (numFields) {
7877             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7878           } else {
7879             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7880           }
7881         }
7882         if (!bSecDof) continue;
7883         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7884         if (bDof) {
7885           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7886           for (PetscInt q = 0; q < bDof; q++, newP++) {
7887             PetscInt a = anchors[bOff + q], aDof = 0;
7888 
7889             if (a >= sStart && a < sEnd) {
7890               if (numFields) {
7891                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7892               } else {
7893                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7894               }
7895             }
7896             if (aDof) {
7897               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7898               for (PetscInt d = 0; d < bSecDof; d++) {
7899                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7900               }
7901             }
7902             oNew += aDof;
7903           }
7904         } else {
7905           // Insert the identity matrix in this block
7906           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7907           oNew += bSecDof;
7908           newP++;
7909         }
7910         o += bSecDof;
7911       }
7912     }
7913 
7914     *outMat = modMat;
7915 
7916     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7917     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7918     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7919     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7920     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7921   }
7922   PetscCall(ISRestoreIndices(aIS, &anchors));
7923 
7924   /* output */
7925   if (outPoints) {
7926     *outPoints = newPoints;
7927   } else {
7928     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7929   }
7930   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7931   PetscFunctionReturn(PETSC_SUCCESS);
7932 }
7933 
7934 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)
7935 {
7936   PetscScalar *modMat        = NULL;
7937   PetscInt     newNumIndices = -1;
7938 
7939   PetscFunctionBegin;
7940   /* 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.
7941      modMat is that matrix C */
7942   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7943   if (outNumIndices) *outNumIndices = newNumIndices;
7944   if (modMat) {
7945     const PetscScalar *newValues = values;
7946 
7947     if (multiplyRight) {
7948       PetscScalar *newNewValues = NULL;
7949       PetscBLASInt M, N, K;
7950       PetscScalar  a = 1.0, b = 0.0;
7951 
7952       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);
7953 
7954       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7955       PetscCall(PetscBLASIntCast(numRows, &N));
7956       PetscCall(PetscBLASIntCast(numIndices, &K));
7957       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7958       // row-major to column-major conversion, right multiplication becomes left multiplication
7959       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7960       numCols   = newNumIndices;
7961       newValues = newNewValues;
7962     }
7963 
7964     if (multiplyLeft) {
7965       PetscScalar *newNewValues = NULL;
7966       PetscBLASInt M, N, K;
7967       PetscScalar  a = 1.0, b = 0.0;
7968 
7969       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);
7970 
7971       PetscCall(PetscBLASIntCast(numCols, &M));
7972       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7973       PetscCall(PetscBLASIntCast(numIndices, &K));
7974       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7975       // row-major to column-major conversion, left multiplication becomes right multiplication
7976       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7977       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7978       newValues = newNewValues;
7979     }
7980     *outValues = (PetscScalar *)newValues;
7981     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7982   }
7983   PetscFunctionReturn(PETSC_SUCCESS);
7984 }
7985 
7986 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)
7987 {
7988   PetscFunctionBegin;
7989   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7990   PetscFunctionReturn(PETSC_SUCCESS);
7991 }
7992 
7993 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7994 {
7995   /* Closure ordering */
7996   PetscSection    clSection;
7997   IS              clPoints;
7998   const PetscInt *clp;
7999   PetscInt       *points;
8000   PetscInt        Ncl, Ni = 0;
8001 
8002   PetscFunctionBeginHot;
8003   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8004   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8005     PetscInt dof;
8006 
8007     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8008     Ni += dof;
8009   }
8010   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8011   *closureSize = Ni;
8012   PetscFunctionReturn(PETSC_SUCCESS);
8013 }
8014 
8015 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)
8016 {
8017   /* Closure ordering */
8018   PetscSection    clSection;
8019   IS              clPoints;
8020   const PetscInt *clp;
8021   PetscInt       *points;
8022   const PetscInt *clperm = NULL;
8023   /* Dof permutation and sign flips */
8024   const PetscInt    **perms[32] = {NULL};
8025   const PetscScalar **flips[32] = {NULL};
8026   PetscScalar        *valCopy   = NULL;
8027   /* Hanging node constraints */
8028   PetscInt    *pointsC = NULL;
8029   PetscScalar *valuesC = NULL;
8030   PetscInt     NclC, NiC;
8031 
8032   PetscInt *idx;
8033   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8034   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8035   PetscInt  idxStart, idxEnd;
8036   PetscInt  nRows, nCols;
8037 
8038   PetscFunctionBeginHot;
8039   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8040   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8041   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8042   PetscAssertPointer(numRows, 6);
8043   PetscAssertPointer(numCols, 7);
8044   if (indices) PetscAssertPointer(indices, 8);
8045   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8046   if (values) PetscAssertPointer(values, 10);
8047   PetscCall(PetscSectionGetNumFields(section, &Nf));
8048   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8049   PetscCall(PetscArrayzero(offsets, 32));
8050   /* 1) Get points in closure */
8051   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8052   if (useClPerm) {
8053     PetscInt depth, clsize;
8054     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8055     for (clsize = 0, p = 0; p < Ncl; p++) {
8056       PetscInt dof;
8057       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8058       clsize += dof;
8059     }
8060     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8061   }
8062   /* 2) Get number of indices on these points and field offsets from section */
8063   for (p = 0; p < Ncl * 2; p += 2) {
8064     PetscInt dof, fdof;
8065 
8066     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8067     for (f = 0; f < Nf; ++f) {
8068       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8069       offsets[f + 1] += fdof;
8070     }
8071     Ni += dof;
8072   }
8073   if (*numRows == -1) *numRows = Ni;
8074   if (*numCols == -1) *numCols = Ni;
8075   nRows = *numRows;
8076   nCols = *numCols;
8077   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8078   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8079   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8080   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8081   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8082   for (f = 0; f < PetscMax(1, Nf); ++f) {
8083     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8084     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8085     /* may need to apply sign changes to the element matrix */
8086     if (values && flips[f]) {
8087       PetscInt foffset = offsets[f];
8088 
8089       for (p = 0; p < Ncl; ++p) {
8090         PetscInt           pnt  = points[2 * p], fdof;
8091         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8092 
8093         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8094         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8095         if (flip) {
8096           PetscInt i, j, k;
8097 
8098           if (!valCopy) {
8099             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8100             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8101             *values = valCopy;
8102           }
8103           for (i = 0; i < fdof; ++i) {
8104             PetscScalar fval = flip[i];
8105 
8106             if (multiplyRight) {
8107               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8108             }
8109             if (multiplyLeft) {
8110               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8111             }
8112           }
8113         }
8114         foffset += fdof;
8115       }
8116     }
8117   }
8118   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8119   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8120   if (NclC) {
8121     if (multiplyRight) *numCols = NiC;
8122     if (multiplyLeft) *numRows = NiC;
8123     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8124     for (f = 0; f < PetscMax(1, Nf); ++f) {
8125       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8126       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8127     }
8128     for (f = 0; f < PetscMax(1, Nf); ++f) {
8129       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8130       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8131     }
8132     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8133     Ncl    = NclC;
8134     Ni     = NiC;
8135     points = pointsC;
8136     if (values) *values = valuesC;
8137   }
8138   /* 5) Calculate indices */
8139   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8140   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8141   if (Nf) {
8142     PetscInt  idxOff;
8143     PetscBool useFieldOffsets;
8144 
8145     if (outOffsets) {
8146       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8147     }
8148     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8149     if (useFieldOffsets) {
8150       for (p = 0; p < Ncl; ++p) {
8151         const PetscInt pnt = points[p * 2];
8152 
8153         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8154       }
8155     } else {
8156       for (p = 0; p < Ncl; ++p) {
8157         const PetscInt pnt = points[p * 2];
8158 
8159         if (pnt < idxStart || pnt >= idxEnd) continue;
8160         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8161         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8162          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8163          * global section. */
8164         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8165       }
8166     }
8167   } else {
8168     PetscInt off = 0, idxOff;
8169 
8170     for (p = 0; p < Ncl; ++p) {
8171       const PetscInt  pnt  = points[p * 2];
8172       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8173 
8174       if (pnt < idxStart || pnt >= idxEnd) continue;
8175       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8176       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8177        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8178       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8179     }
8180   }
8181   /* 6) Cleanup */
8182   for (f = 0; f < PetscMax(1, Nf); ++f) {
8183     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8184     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8185   }
8186   if (NclC) {
8187     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8188   } else {
8189     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8190   }
8191 
8192   if (indices) *indices = idx;
8193   PetscFunctionReturn(PETSC_SUCCESS);
8194 }
8195 
8196 /*@C
8197   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8198 
8199   Not collective
8200 
8201   Input Parameters:
8202 + dm         - The `DM`
8203 . section    - The `PetscSection` describing the points (a local section)
8204 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8205 . point      - The point defining the closure
8206 - useClPerm  - Use the closure point permutation if available
8207 
8208   Output Parameters:
8209 + numIndices - The number of dof indices in the closure of point with the input sections
8210 . indices    - The dof indices
8211 . outOffsets - Array to write the field offsets into, or `NULL`
8212 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8213 
8214   Level: advanced
8215 
8216   Notes:
8217   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8218 
8219   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8220   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8221   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8222   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8223   indices (with the above semantics) are implied.
8224 
8225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8226           `PetscSection`, `DMGetGlobalSection()`
8227 @*/
8228 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8229 {
8230   PetscInt numRows = -1, numCols = -1;
8231 
8232   PetscFunctionBeginHot;
8233   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8234   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8235   *numIndices = numRows;
8236   PetscFunctionReturn(PETSC_SUCCESS);
8237 }
8238 
8239 /*@C
8240   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8241 
8242   Not collective
8243 
8244   Input Parameters:
8245 + dm         - The `DM`
8246 . section    - The `PetscSection` describing the points (a local section)
8247 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8248 . point      - The point defining the closure
8249 - useClPerm  - Use the closure point permutation if available
8250 
8251   Output Parameters:
8252 + numIndices - The number of dof indices in the closure of point with the input sections
8253 . indices    - The dof indices
8254 . outOffsets - Array to write the field offsets into, or `NULL`
8255 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8256 
8257   Level: advanced
8258 
8259   Notes:
8260   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8261 
8262   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8263   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8264   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8265   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8266   indices (with the above semantics) are implied.
8267 
8268 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8269 @*/
8270 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8271 {
8272   PetscFunctionBegin;
8273   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8274   PetscAssertPointer(indices, 7);
8275   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8276   PetscFunctionReturn(PETSC_SUCCESS);
8277 }
8278 
8279 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8280 {
8281   DM_Plex           *mesh = (DM_Plex *)dm->data;
8282   PetscInt          *indices;
8283   PetscInt           numIndices;
8284   const PetscScalar *valuesOrig = values;
8285   PetscErrorCode     ierr;
8286 
8287   PetscFunctionBegin;
8288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8289   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8290   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8291   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8292   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8293   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8294 
8295   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8296 
8297   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8298   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8299   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8300   if (ierr) {
8301     PetscMPIInt rank;
8302 
8303     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8304     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8305     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8306     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8307     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8308     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8309   }
8310   if (mesh->printFEM > 1) {
8311     PetscInt i;
8312     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8313     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8314     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8315   }
8316 
8317   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8318   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8319   PetscFunctionReturn(PETSC_SUCCESS);
8320 }
8321 
8322 /*@C
8323   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8324 
8325   Not collective
8326 
8327   Input Parameters:
8328 + dm            - The `DM`
8329 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8330 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8331 . A             - The matrix
8332 . point         - The point in the `DM`
8333 . values        - The array of values
8334 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8335 
8336   Level: intermediate
8337 
8338 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8339 @*/
8340 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8341 {
8342   PetscFunctionBegin;
8343   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8344   PetscFunctionReturn(PETSC_SUCCESS);
8345 }
8346 
8347 /*@C
8348   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8349 
8350   Not collective
8351 
8352   Input Parameters:
8353 + dmRow            - The `DM` for the row fields
8354 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8355 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8356 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8357 . dmCol            - The `DM` for the column fields
8358 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8359 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8360 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8361 . A                - The matrix
8362 . point            - The point in the `DM`
8363 . values           - The array of values
8364 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8365 
8366   Level: intermediate
8367 
8368 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8369 @*/
8370 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)
8371 {
8372   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8373   PetscInt          *indicesRow, *indicesCol;
8374   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8375   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8376 
8377   PetscErrorCode ierr;
8378 
8379   PetscFunctionBegin;
8380   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8381   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8382   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8383   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8384   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8385   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8386   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8387   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8388   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8389   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8390   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8391 
8392   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8393   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8394   valuesV1 = valuesV0;
8395   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8396   valuesV2 = valuesV1;
8397   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8398 
8399   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8400   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8401   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8402   if (ierr) {
8403     PetscMPIInt rank;
8404 
8405     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8406     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8407     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8408     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8409     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8410     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8411     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8412   }
8413 
8414   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8415   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8416   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8417   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8418   PetscFunctionReturn(PETSC_SUCCESS);
8419 }
8420 
8421 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8422 {
8423   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8424   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8425   PetscInt       *cpoints = NULL;
8426   PetscInt       *findices, *cindices;
8427   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8428   PetscInt        foffsets[32], coffsets[32];
8429   DMPolytopeType  ct;
8430   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8431   PetscErrorCode  ierr;
8432 
8433   PetscFunctionBegin;
8434   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8435   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8436   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8437   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8438   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8439   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8440   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8441   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8442   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8443   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8444   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8445   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8446   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8447   PetscCall(PetscArrayzero(foffsets, 32));
8448   PetscCall(PetscArrayzero(coffsets, 32));
8449   /* Column indices */
8450   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8451   maxFPoints = numCPoints;
8452   /* Compress out points not in the section */
8453   /*   TODO: Squeeze out points with 0 dof as well */
8454   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8455   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8456     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8457       cpoints[q * 2]     = cpoints[p];
8458       cpoints[q * 2 + 1] = cpoints[p + 1];
8459       ++q;
8460     }
8461   }
8462   numCPoints = q;
8463   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8464     PetscInt fdof;
8465 
8466     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8467     if (!dof) continue;
8468     for (f = 0; f < numFields; ++f) {
8469       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8470       coffsets[f + 1] += fdof;
8471     }
8472     numCIndices += dof;
8473   }
8474   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8475   /* Row indices */
8476   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8477   {
8478     DMPlexTransform tr;
8479     DMPolytopeType *rct;
8480     PetscInt       *rsize, *rcone, *rornt, Nt;
8481 
8482     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8483     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8484     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8485     numSubcells = rsize[Nt - 1];
8486     PetscCall(DMPlexTransformDestroy(&tr));
8487   }
8488   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8489   for (r = 0, q = 0; r < numSubcells; ++r) {
8490     /* TODO Map from coarse to fine cells */
8491     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8492     /* Compress out points not in the section */
8493     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8494     for (p = 0; p < numFPoints * 2; p += 2) {
8495       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8496         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8497         if (!dof) continue;
8498         for (s = 0; s < q; ++s)
8499           if (fpoints[p] == ftotpoints[s * 2]) break;
8500         if (s < q) continue;
8501         ftotpoints[q * 2]     = fpoints[p];
8502         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8503         ++q;
8504       }
8505     }
8506     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8507   }
8508   numFPoints = q;
8509   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8510     PetscInt fdof;
8511 
8512     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8513     if (!dof) continue;
8514     for (f = 0; f < numFields; ++f) {
8515       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8516       foffsets[f + 1] += fdof;
8517     }
8518     numFIndices += dof;
8519   }
8520   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8521 
8522   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8523   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8524   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8525   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8526   if (numFields) {
8527     const PetscInt **permsF[32] = {NULL};
8528     const PetscInt **permsC[32] = {NULL};
8529 
8530     for (f = 0; f < numFields; f++) {
8531       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8532       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8533     }
8534     for (p = 0; p < numFPoints; p++) {
8535       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8536       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8537     }
8538     for (p = 0; p < numCPoints; p++) {
8539       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8540       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8541     }
8542     for (f = 0; f < numFields; f++) {
8543       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8544       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8545     }
8546   } else {
8547     const PetscInt **permsF = NULL;
8548     const PetscInt **permsC = NULL;
8549 
8550     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8551     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8552     for (p = 0, off = 0; p < numFPoints; p++) {
8553       const PetscInt *perm = permsF ? permsF[p] : NULL;
8554 
8555       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8556       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8557     }
8558     for (p = 0, off = 0; p < numCPoints; p++) {
8559       const PetscInt *perm = permsC ? permsC[p] : NULL;
8560 
8561       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8562       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8563     }
8564     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8565     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8566   }
8567   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8568   /* TODO: flips */
8569   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8570   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8571   if (ierr) {
8572     PetscMPIInt rank;
8573 
8574     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8575     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8576     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8577     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8578     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8579   }
8580   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8581   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8582   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8583   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8584   PetscFunctionReturn(PETSC_SUCCESS);
8585 }
8586 
8587 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8588 {
8589   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8590   PetscInt       *cpoints      = NULL;
8591   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8592   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8593   DMPolytopeType  ct;
8594   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8595 
8596   PetscFunctionBegin;
8597   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8598   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8599   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8600   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8601   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8602   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8603   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8604   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8605   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8606   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8607   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8608   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8609   /* Column indices */
8610   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8611   maxFPoints = numCPoints;
8612   /* Compress out points not in the section */
8613   /*   TODO: Squeeze out points with 0 dof as well */
8614   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8615   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8616     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8617       cpoints[q * 2]     = cpoints[p];
8618       cpoints[q * 2 + 1] = cpoints[p + 1];
8619       ++q;
8620     }
8621   }
8622   numCPoints = q;
8623   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8624     PetscInt fdof;
8625 
8626     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8627     if (!dof) continue;
8628     for (f = 0; f < numFields; ++f) {
8629       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8630       coffsets[f + 1] += fdof;
8631     }
8632     numCIndices += dof;
8633   }
8634   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8635   /* Row indices */
8636   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8637   {
8638     DMPlexTransform tr;
8639     DMPolytopeType *rct;
8640     PetscInt       *rsize, *rcone, *rornt, Nt;
8641 
8642     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8643     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8644     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8645     numSubcells = rsize[Nt - 1];
8646     PetscCall(DMPlexTransformDestroy(&tr));
8647   }
8648   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8649   for (r = 0, q = 0; r < numSubcells; ++r) {
8650     /* TODO Map from coarse to fine cells */
8651     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8652     /* Compress out points not in the section */
8653     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8654     for (p = 0; p < numFPoints * 2; p += 2) {
8655       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8656         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8657         if (!dof) continue;
8658         for (s = 0; s < q; ++s)
8659           if (fpoints[p] == ftotpoints[s * 2]) break;
8660         if (s < q) continue;
8661         ftotpoints[q * 2]     = fpoints[p];
8662         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8663         ++q;
8664       }
8665     }
8666     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8667   }
8668   numFPoints = q;
8669   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8670     PetscInt fdof;
8671 
8672     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8673     if (!dof) continue;
8674     for (f = 0; f < numFields; ++f) {
8675       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8676       foffsets[f + 1] += fdof;
8677     }
8678     numFIndices += dof;
8679   }
8680   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8681 
8682   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8683   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8684   if (numFields) {
8685     const PetscInt **permsF[32] = {NULL};
8686     const PetscInt **permsC[32] = {NULL};
8687 
8688     for (f = 0; f < numFields; f++) {
8689       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8690       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8691     }
8692     for (p = 0; p < numFPoints; p++) {
8693       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8694       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8695     }
8696     for (p = 0; p < numCPoints; p++) {
8697       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8698       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8699     }
8700     for (f = 0; f < numFields; f++) {
8701       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8702       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8703     }
8704   } else {
8705     const PetscInt **permsF = NULL;
8706     const PetscInt **permsC = NULL;
8707 
8708     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8709     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8710     for (p = 0, off = 0; p < numFPoints; p++) {
8711       const PetscInt *perm = permsF ? permsF[p] : NULL;
8712 
8713       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8714       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8715     }
8716     for (p = 0, off = 0; p < numCPoints; p++) {
8717       const PetscInt *perm = permsC ? permsC[p] : NULL;
8718 
8719       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8720       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8721     }
8722     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8723     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8724   }
8725   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8726   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8727   PetscFunctionReturn(PETSC_SUCCESS);
8728 }
8729 
8730 /*@
8731   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8732 
8733   Input Parameter:
8734 . dm - The `DMPLEX` object
8735 
8736   Output Parameter:
8737 . cellHeight - The height of a cell
8738 
8739   Level: developer
8740 
8741 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8742 @*/
8743 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8744 {
8745   DM_Plex *mesh = (DM_Plex *)dm->data;
8746 
8747   PetscFunctionBegin;
8748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8749   PetscAssertPointer(cellHeight, 2);
8750   *cellHeight = mesh->vtkCellHeight;
8751   PetscFunctionReturn(PETSC_SUCCESS);
8752 }
8753 
8754 /*@
8755   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8756 
8757   Input Parameters:
8758 + dm         - The `DMPLEX` object
8759 - cellHeight - The height of a cell
8760 
8761   Level: developer
8762 
8763 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8764 @*/
8765 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8766 {
8767   DM_Plex *mesh = (DM_Plex *)dm->data;
8768 
8769   PetscFunctionBegin;
8770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8771   mesh->vtkCellHeight = cellHeight;
8772   PetscFunctionReturn(PETSC_SUCCESS);
8773 }
8774 
8775 /*@
8776   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8777 
8778   Input Parameters:
8779 + dm - The `DMPLEX` object
8780 - ct - The `DMPolytopeType` of the cell
8781 
8782   Output Parameters:
8783 + start - The first cell of this type, or `NULL`
8784 - end   - The upper bound on this celltype, or `NULL`
8785 
8786   Level: advanced
8787 
8788 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8789 @*/
8790 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8791 {
8792   DM_Plex *mesh = (DM_Plex *)dm->data;
8793   DMLabel  label;
8794   PetscInt pStart, pEnd;
8795 
8796   PetscFunctionBegin;
8797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8798   if (start) {
8799     PetscAssertPointer(start, 3);
8800     *start = 0;
8801   }
8802   if (end) {
8803     PetscAssertPointer(end, 4);
8804     *end = 0;
8805   }
8806   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8807   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8808   if (mesh->tr) {
8809     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8810   } else {
8811     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8812     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8813     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8814   }
8815   PetscFunctionReturn(PETSC_SUCCESS);
8816 }
8817 
8818 /*@
8819   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8820 
8821   Input Parameters:
8822 + dm    - The `DMPLEX` object
8823 - depth - The depth for the given point stratum
8824 
8825   Output Parameter:
8826 . gsize - The global number of points in the stratum
8827 
8828   Level: advanced
8829 
8830 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8831 @*/
8832 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8833 {
8834   PetscSF         sf;
8835   const PetscInt *leaves;
8836   PetscInt        Nl, loc, start, end, lsize = 0;
8837 
8838   PetscFunctionBegin;
8839   PetscCall(DMGetPointSF(dm, &sf));
8840   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8841   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8842   for (PetscInt p = start; p < end; ++p) {
8843     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8844     if (loc < 0) ++lsize;
8845   }
8846   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8847   PetscFunctionReturn(PETSC_SUCCESS);
8848 }
8849 
8850 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8851 {
8852   PetscSection section, globalSection;
8853   PetscInt    *numbers, p;
8854 
8855   PetscFunctionBegin;
8856   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8857   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8858   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8859   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8860   PetscCall(PetscSectionSetUp(section));
8861   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8862   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8863   for (p = pStart; p < pEnd; ++p) {
8864     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8865     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8866     else numbers[p - pStart] += shift;
8867   }
8868   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8869   if (globalSize) {
8870     PetscLayout layout;
8871     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8872     PetscCall(PetscLayoutGetSize(layout, globalSize));
8873     PetscCall(PetscLayoutDestroy(&layout));
8874   }
8875   PetscCall(PetscSectionDestroy(&section));
8876   PetscCall(PetscSectionDestroy(&globalSection));
8877   PetscFunctionReturn(PETSC_SUCCESS);
8878 }
8879 
8880 /*@
8881   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8882 
8883   Input Parameters:
8884 + dm         - The `DMPLEX` object
8885 - includeAll - Whether to include all cells, or just the simplex and box cells
8886 
8887   Output Parameter:
8888 . globalCellNumbers - Global cell numbers for all cells on this process
8889 
8890   Level: developer
8891 
8892 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8893 @*/
8894 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8895 {
8896   PetscInt cellHeight, cStart, cEnd;
8897 
8898   PetscFunctionBegin;
8899   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8900   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8901   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8902   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8903   PetscFunctionReturn(PETSC_SUCCESS);
8904 }
8905 
8906 /*@
8907   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8908 
8909   Input Parameter:
8910 . dm - The `DMPLEX` object
8911 
8912   Output Parameter:
8913 . globalCellNumbers - Global cell numbers for all cells on this process
8914 
8915   Level: developer
8916 
8917 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8918 @*/
8919 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8920 {
8921   DM_Plex *mesh = (DM_Plex *)dm->data;
8922 
8923   PetscFunctionBegin;
8924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8925   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8926   *globalCellNumbers = mesh->globalCellNumbers;
8927   PetscFunctionReturn(PETSC_SUCCESS);
8928 }
8929 
8930 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8931 {
8932   PetscInt vStart, vEnd;
8933 
8934   PetscFunctionBegin;
8935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8936   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8937   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8938   PetscFunctionReturn(PETSC_SUCCESS);
8939 }
8940 
8941 /*@
8942   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8943 
8944   Input Parameter:
8945 . dm - The `DMPLEX` object
8946 
8947   Output Parameter:
8948 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8949 
8950   Level: developer
8951 
8952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8953 @*/
8954 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8955 {
8956   DM_Plex *mesh = (DM_Plex *)dm->data;
8957 
8958   PetscFunctionBegin;
8959   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8960   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8961   *globalVertexNumbers = mesh->globalVertexNumbers;
8962   PetscFunctionReturn(PETSC_SUCCESS);
8963 }
8964 
8965 /*@
8966   DMPlexCreatePointNumbering - Create a global numbering for all points.
8967 
8968   Collective
8969 
8970   Input Parameter:
8971 . dm - The `DMPLEX` object
8972 
8973   Output Parameter:
8974 . globalPointNumbers - Global numbers for all points on this process
8975 
8976   Level: developer
8977 
8978   Notes:
8979   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8980   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8981   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8982   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8983 
8984   The partitioned mesh is
8985   ```
8986   (2)--0--(3)--1--(4)    (1)--0--(2)
8987   ```
8988   and its global numbering is
8989   ```
8990   (3)--0--(4)--1--(5)--2--(6)
8991   ```
8992   Then the global numbering is provided as
8993   ```
8994   [0] Number of indices in set 5
8995   [0] 0 0
8996   [0] 1 1
8997   [0] 2 3
8998   [0] 3 4
8999   [0] 4 -6
9000   [1] Number of indices in set 3
9001   [1] 0 2
9002   [1] 1 5
9003   [1] 2 6
9004   ```
9005 
9006 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9007 @*/
9008 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9009 {
9010   IS        nums[4];
9011   PetscInt  depths[4], gdepths[4], starts[4];
9012   PetscInt  depth, d, shift = 0;
9013   PetscBool empty = PETSC_FALSE;
9014 
9015   PetscFunctionBegin;
9016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9017   PetscCall(DMPlexGetDepth(dm, &depth));
9018   // For unstratified meshes use dim instead of depth
9019   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9020   // If any stratum is empty, we must mark all empty
9021   for (d = 0; d <= depth; ++d) {
9022     PetscInt end;
9023 
9024     depths[d] = depth - d;
9025     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9026     if (!(starts[d] - end)) empty = PETSC_TRUE;
9027   }
9028   if (empty)
9029     for (d = 0; d <= depth; ++d) {
9030       depths[d] = -1;
9031       starts[d] = -1;
9032     }
9033   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9034   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9035   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]);
9036   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9037   for (d = 0; d <= depth; ++d) {
9038     PetscInt pStart, pEnd, gsize;
9039 
9040     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9041     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9042     shift += gsize;
9043   }
9044   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9045   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9046   PetscFunctionReturn(PETSC_SUCCESS);
9047 }
9048 
9049 /*@
9050   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9051 
9052   Collective
9053 
9054   Input Parameter:
9055 . dm - The `DMPLEX` object
9056 
9057   Output Parameter:
9058 . globalEdgeNumbers - Global numbers for all edges on this process
9059 
9060   Level: developer
9061 
9062   Notes:
9063   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).
9064 
9065 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9066 @*/
9067 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9068 {
9069   PetscSF  sf;
9070   PetscInt eStart, eEnd;
9071 
9072   PetscFunctionBegin;
9073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9074   PetscCall(DMGetPointSF(dm, &sf));
9075   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9076   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9077   PetscFunctionReturn(PETSC_SUCCESS);
9078 }
9079 
9080 /*@
9081   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9082 
9083   Input Parameter:
9084 . dm - The `DMPLEX` object
9085 
9086   Output Parameter:
9087 . ranks - The rank field
9088 
9089   Options Database Key:
9090 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9091 
9092   Level: intermediate
9093 
9094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9095 @*/
9096 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9097 {
9098   DM             rdm;
9099   PetscFE        fe;
9100   PetscScalar   *r;
9101   PetscMPIInt    rank;
9102   DMPolytopeType ct;
9103   PetscInt       dim, cStart, cEnd, c;
9104   PetscBool      simplex;
9105 
9106   PetscFunctionBeginUser;
9107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9108   PetscAssertPointer(ranks, 2);
9109   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9110   PetscCall(DMClone(dm, &rdm));
9111   PetscCall(DMGetDimension(rdm, &dim));
9112   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9113   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9114   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9115   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9116   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9117   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9118   PetscCall(PetscFEDestroy(&fe));
9119   PetscCall(DMCreateDS(rdm));
9120   PetscCall(DMCreateGlobalVector(rdm, ranks));
9121   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9122   PetscCall(VecGetArray(*ranks, &r));
9123   for (c = cStart; c < cEnd; ++c) {
9124     PetscScalar *lr;
9125 
9126     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9127     if (lr) *lr = rank;
9128   }
9129   PetscCall(VecRestoreArray(*ranks, &r));
9130   PetscCall(DMDestroy(&rdm));
9131   PetscFunctionReturn(PETSC_SUCCESS);
9132 }
9133 
9134 /*@
9135   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9136 
9137   Input Parameters:
9138 + dm    - The `DMPLEX`
9139 - label - The `DMLabel`
9140 
9141   Output Parameter:
9142 . val - The label value field
9143 
9144   Options Database Key:
9145 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9146 
9147   Level: intermediate
9148 
9149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9150 @*/
9151 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9152 {
9153   DM             rdm, plex;
9154   Vec            lval;
9155   PetscSection   section;
9156   PetscFE        fe;
9157   PetscScalar   *v;
9158   PetscInt       dim, pStart, pEnd, p, cStart;
9159   DMPolytopeType ct;
9160   char           name[PETSC_MAX_PATH_LEN];
9161   const char    *lname, *prefix;
9162 
9163   PetscFunctionBeginUser;
9164   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9165   PetscAssertPointer(label, 2);
9166   PetscAssertPointer(val, 3);
9167   PetscCall(DMClone(dm, &rdm));
9168   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9169   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9170   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9171   PetscCall(DMDestroy(&plex));
9172   PetscCall(DMGetDimension(rdm, &dim));
9173   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9174   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9175   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9176   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9177   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9178   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9179   PetscCall(PetscFEDestroy(&fe));
9180   PetscCall(DMCreateDS(rdm));
9181   PetscCall(DMCreateGlobalVector(rdm, val));
9182   PetscCall(DMCreateLocalVector(rdm, &lval));
9183   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9184   PetscCall(DMGetLocalSection(rdm, &section));
9185   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9186   PetscCall(VecGetArray(lval, &v));
9187   for (p = pStart; p < pEnd; ++p) {
9188     PetscInt cval, dof, off;
9189 
9190     PetscCall(PetscSectionGetDof(section, p, &dof));
9191     if (!dof) continue;
9192     PetscCall(DMLabelGetValue(label, p, &cval));
9193     PetscCall(PetscSectionGetOffset(section, p, &off));
9194     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9195   }
9196   PetscCall(VecRestoreArray(lval, &v));
9197   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9198   PetscCall(VecDestroy(&lval));
9199   PetscCall(DMDestroy(&rdm));
9200   PetscFunctionReturn(PETSC_SUCCESS);
9201 }
9202 
9203 /*@
9204   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9205 
9206   Input Parameter:
9207 . dm - The `DMPLEX` object
9208 
9209   Level: developer
9210 
9211   Notes:
9212   This is a useful diagnostic when creating meshes programmatically.
9213 
9214   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9215 
9216 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9217 @*/
9218 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9219 {
9220   PetscSection    coneSection, supportSection;
9221   const PetscInt *cone, *support;
9222   PetscInt        coneSize, c, supportSize, s;
9223   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9224   PetscBool       storagecheck = PETSC_TRUE;
9225 
9226   PetscFunctionBegin;
9227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9228   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9229   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9230   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9231   /* Check that point p is found in the support of its cone points, and vice versa */
9232   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9233   for (p = pStart; p < pEnd; ++p) {
9234     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9235     PetscCall(DMPlexGetCone(dm, p, &cone));
9236     for (c = 0; c < coneSize; ++c) {
9237       PetscBool dup = PETSC_FALSE;
9238       PetscInt  d;
9239       for (d = c - 1; d >= 0; --d) {
9240         if (cone[c] == cone[d]) {
9241           dup = PETSC_TRUE;
9242           break;
9243         }
9244       }
9245       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9246       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9247       for (s = 0; s < supportSize; ++s) {
9248         if (support[s] == p) break;
9249       }
9250       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9251         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9252         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9253         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9254         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9255         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9256         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9257         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]);
9258         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9259       }
9260     }
9261     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9262     if (p != pp) {
9263       storagecheck = PETSC_FALSE;
9264       continue;
9265     }
9266     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9267     PetscCall(DMPlexGetSupport(dm, p, &support));
9268     for (s = 0; s < supportSize; ++s) {
9269       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9270       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9271       for (c = 0; c < coneSize; ++c) {
9272         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9273         if (cone[c] != pp) {
9274           c = 0;
9275           break;
9276         }
9277         if (cone[c] == p) break;
9278       }
9279       if (c >= coneSize) {
9280         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9281         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9282         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9283         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9284         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9285         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9286         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9287       }
9288     }
9289   }
9290   if (storagecheck) {
9291     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9292     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9293     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9294   }
9295   PetscFunctionReturn(PETSC_SUCCESS);
9296 }
9297 
9298 /*
9299   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.
9300 */
9301 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9302 {
9303   DMPolytopeType  cct;
9304   PetscInt        ptpoints[4];
9305   const PetscInt *cone, *ccone, *ptcone;
9306   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9307 
9308   PetscFunctionBegin;
9309   *unsplit = 0;
9310   switch (ct) {
9311   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9312     ptpoints[npt++] = c;
9313     break;
9314   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9315     PetscCall(DMPlexGetCone(dm, c, &cone));
9316     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9317     for (cp = 0; cp < coneSize; ++cp) {
9318       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9319       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9320     }
9321     break;
9322   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9323   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9324     PetscCall(DMPlexGetCone(dm, c, &cone));
9325     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9326     for (cp = 0; cp < coneSize; ++cp) {
9327       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9328       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9329       for (ccp = 0; ccp < cconeSize; ++ccp) {
9330         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9331         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9332           PetscInt p;
9333           for (p = 0; p < npt; ++p)
9334             if (ptpoints[p] == ccone[ccp]) break;
9335           if (p == npt) ptpoints[npt++] = ccone[ccp];
9336         }
9337       }
9338     }
9339     break;
9340   default:
9341     break;
9342   }
9343   for (pt = 0; pt < npt; ++pt) {
9344     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9345     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9346   }
9347   PetscFunctionReturn(PETSC_SUCCESS);
9348 }
9349 
9350 /*@
9351   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9352 
9353   Input Parameters:
9354 + dm         - The `DMPLEX` object
9355 - cellHeight - Normally 0
9356 
9357   Level: developer
9358 
9359   Notes:
9360   This is a useful diagnostic when creating meshes programmatically.
9361   Currently applicable only to homogeneous simplex or tensor meshes.
9362 
9363   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9364 
9365 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9366 @*/
9367 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9368 {
9369   DMPlexInterpolatedFlag interp;
9370   DMPolytopeType         ct;
9371   PetscInt               vStart, vEnd, cStart, cEnd, c;
9372 
9373   PetscFunctionBegin;
9374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9375   PetscCall(DMPlexIsInterpolated(dm, &interp));
9376   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9377   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9378   for (c = cStart; c < cEnd; ++c) {
9379     PetscInt *closure = NULL;
9380     PetscInt  coneSize, closureSize, cl, Nv = 0;
9381 
9382     PetscCall(DMPlexGetCellType(dm, c, &ct));
9383     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9384     if (interp == DMPLEX_INTERPOLATED_FULL) {
9385       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9386       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));
9387     }
9388     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9389     for (cl = 0; cl < closureSize * 2; cl += 2) {
9390       const PetscInt p = closure[cl];
9391       if ((p >= vStart) && (p < vEnd)) ++Nv;
9392     }
9393     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9394     /* Special Case: Tensor faces with identified vertices */
9395     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9396       PetscInt unsplit;
9397 
9398       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9399       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9400     }
9401     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));
9402   }
9403   PetscFunctionReturn(PETSC_SUCCESS);
9404 }
9405 
9406 /*@
9407   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9408 
9409   Collective
9410 
9411   Input Parameters:
9412 + dm         - The `DMPLEX` object
9413 - cellHeight - Normally 0
9414 
9415   Level: developer
9416 
9417   Notes:
9418   This is a useful diagnostic when creating meshes programmatically.
9419   This routine is only relevant for meshes that are fully interpolated across all ranks.
9420   It will error out if a partially interpolated mesh is given on some rank.
9421   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9422 
9423   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9424 
9425 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9426 @*/
9427 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9428 {
9429   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9430   DMPlexInterpolatedFlag interpEnum;
9431 
9432   PetscFunctionBegin;
9433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9434   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9435   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9436   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9437     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9438     PetscFunctionReturn(PETSC_SUCCESS);
9439   }
9440 
9441   PetscCall(DMGetDimension(dm, &dim));
9442   PetscCall(DMPlexGetDepth(dm, &depth));
9443   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9444   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9445     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9446     for (c = cStart; c < cEnd; ++c) {
9447       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9448       const DMPolytopeType *faceTypes;
9449       DMPolytopeType        ct;
9450       PetscInt              numFaces, coneSize, f;
9451       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9452 
9453       PetscCall(DMPlexGetCellType(dm, c, &ct));
9454       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9455       if (unsplit) continue;
9456       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9457       PetscCall(DMPlexGetCone(dm, c, &cone));
9458       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9459       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9460       for (cl = 0; cl < closureSize * 2; cl += 2) {
9461         const PetscInt p = closure[cl];
9462         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9463       }
9464       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9465       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);
9466       for (f = 0; f < numFaces; ++f) {
9467         DMPolytopeType fct;
9468         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9469 
9470         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9471         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9472         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9473           const PetscInt p = fclosure[cl];
9474           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9475         }
9476         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]);
9477         for (v = 0; v < fnumCorners; ++v) {
9478           if (fclosure[v] != faces[fOff + v]) {
9479             PetscInt v1;
9480 
9481             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9482             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9483             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9484             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9485             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9486             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]);
9487           }
9488         }
9489         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9490         fOff += faceSizes[f];
9491       }
9492       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9493       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9494     }
9495   }
9496   PetscFunctionReturn(PETSC_SUCCESS);
9497 }
9498 
9499 /*@
9500   DMPlexCheckGeometry - Check the geometry of mesh cells
9501 
9502   Input Parameter:
9503 . dm - The `DMPLEX` object
9504 
9505   Level: developer
9506 
9507   Notes:
9508   This is a useful diagnostic when creating meshes programmatically.
9509 
9510   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9511 
9512 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9513 @*/
9514 PetscErrorCode DMPlexCheckGeometry(DM dm)
9515 {
9516   Vec       coordinates;
9517   PetscReal detJ, J[9], refVol = 1.0;
9518   PetscReal vol;
9519   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9520 
9521   PetscFunctionBegin;
9522   PetscCall(DMGetDimension(dm, &dim));
9523   PetscCall(DMGetCoordinateDim(dm, &dE));
9524   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9525   PetscCall(DMPlexGetDepth(dm, &depth));
9526   for (d = 0; d < dim; ++d) refVol *= 2.0;
9527   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9528   /* Make sure local coordinates are created, because that step is collective */
9529   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9530   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9531   for (c = cStart; c < cEnd; ++c) {
9532     DMPolytopeType ct;
9533     PetscInt       unsplit;
9534     PetscBool      ignoreZeroVol = PETSC_FALSE;
9535 
9536     PetscCall(DMPlexGetCellType(dm, c, &ct));
9537     switch (ct) {
9538     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9539     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9540     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9541       ignoreZeroVol = PETSC_TRUE;
9542       break;
9543     default:
9544       break;
9545     }
9546     switch (ct) {
9547     case DM_POLYTOPE_TRI_PRISM:
9548     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9549     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9550     case DM_POLYTOPE_PYRAMID:
9551       continue;
9552     default:
9553       break;
9554     }
9555     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9556     if (unsplit) continue;
9557     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9558     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);
9559     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9560     /* This should work with periodicity since DG coordinates should be used */
9561     if (depth > 1) {
9562       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9563       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);
9564       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9565     }
9566   }
9567   PetscFunctionReturn(PETSC_SUCCESS);
9568 }
9569 
9570 /*@
9571   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9572 
9573   Collective
9574 
9575   Input Parameters:
9576 + dm              - The `DMPLEX` object
9577 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9578 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9579 
9580   Level: developer
9581 
9582   Notes:
9583   This is mainly intended for debugging/testing purposes.
9584 
9585   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9586 
9587   Extra roots can come from periodic cuts, where additional points appear on the boundary
9588 
9589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9590 @*/
9591 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9592 {
9593   PetscInt           l, nleaves, nroots, overlap;
9594   const PetscInt    *locals;
9595   const PetscSFNode *remotes;
9596   PetscBool          distributed;
9597   MPI_Comm           comm;
9598   PetscMPIInt        rank;
9599 
9600   PetscFunctionBegin;
9601   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9602   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9603   else pointSF = dm->sf;
9604   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9605   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9606   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9607   {
9608     PetscMPIInt mpiFlag;
9609 
9610     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9611     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9612   }
9613   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9614   PetscCall(DMPlexIsDistributed(dm, &distributed));
9615   if (!distributed) {
9616     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);
9617     PetscFunctionReturn(PETSC_SUCCESS);
9618   }
9619   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);
9620   PetscCall(DMPlexGetOverlap(dm, &overlap));
9621 
9622   /* Check SF graph is compatible with DMPlex chart */
9623   {
9624     PetscInt pStart, pEnd, maxLeaf;
9625 
9626     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9627     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9628     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9629     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9630   }
9631 
9632   /* Check Point SF has no local points referenced */
9633   for (l = 0; l < nleaves; l++) {
9634     PetscMPIInt irank;
9635 
9636     PetscCall(PetscMPIIntCast(remotes[l].rank, &irank));
9637     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);
9638   }
9639 
9640   /* Check there are no cells in interface */
9641   if (!overlap) {
9642     PetscInt cellHeight, cStart, cEnd;
9643 
9644     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9645     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9646     for (l = 0; l < nleaves; ++l) {
9647       const PetscInt point = locals ? locals[l] : l;
9648 
9649       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9650     }
9651   }
9652 
9653   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9654   {
9655     const PetscInt *rootdegree;
9656 
9657     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9658     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9659     for (l = 0; l < nleaves; ++l) {
9660       const PetscInt  point = locals ? locals[l] : l;
9661       const PetscInt *cone;
9662       PetscInt        coneSize, c, idx;
9663 
9664       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9665       PetscCall(DMPlexGetCone(dm, point, &cone));
9666       for (c = 0; c < coneSize; ++c) {
9667         if (!rootdegree[cone[c]]) {
9668           if (locals) {
9669             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9670           } else {
9671             idx = (cone[c] < nleaves) ? cone[c] : -1;
9672           }
9673           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9674         }
9675       }
9676     }
9677   }
9678   PetscFunctionReturn(PETSC_SUCCESS);
9679 }
9680 
9681 /*@
9682   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9683 
9684   Collective
9685 
9686   Input Parameter:
9687 . dm - The `DMPLEX` object
9688 
9689   Level: developer
9690 
9691   Notes:
9692   This is mainly intended for debugging/testing purposes.
9693 
9694   Other cell types which are disconnected would be caught by the symmetry and face checks.
9695 
9696   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9697 
9698 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9699 @*/
9700 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9701 {
9702   PetscInt pStart, pEnd, vStart, vEnd;
9703 
9704   PetscFunctionBegin;
9705   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9706   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9707   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9708   for (PetscInt v = vStart; v < vEnd; ++v) {
9709     PetscInt suppSize;
9710 
9711     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9712     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9713   }
9714   PetscFunctionReturn(PETSC_SUCCESS);
9715 }
9716 
9717 /*@
9718   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9719 
9720   Input Parameter:
9721 . dm - The `DMPLEX` object
9722 
9723   Level: developer
9724 
9725   Notes:
9726   This is a useful diagnostic when creating meshes programmatically.
9727 
9728   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9729 
9730   Currently does not include `DMPlexCheckCellShape()`.
9731 
9732 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9733 @*/
9734 PetscErrorCode DMPlexCheck(DM dm)
9735 {
9736   PetscInt cellHeight;
9737 
9738   PetscFunctionBegin;
9739   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9740   PetscCall(DMPlexCheckSymmetry(dm));
9741   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9742   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9743   PetscCall(DMPlexCheckGeometry(dm));
9744   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9745   PetscCall(DMPlexCheckInterfaceCones(dm));
9746   PetscCall(DMPlexCheckOrphanVertices(dm));
9747   PetscFunctionReturn(PETSC_SUCCESS);
9748 }
9749 
9750 typedef struct cell_stats {
9751   PetscReal min, max, sum, squaresum;
9752   PetscInt  count;
9753 } cell_stats_t;
9754 
9755 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9756 {
9757   PetscInt i, N = *len;
9758 
9759   for (i = 0; i < N; i++) {
9760     cell_stats_t *A = (cell_stats_t *)a;
9761     cell_stats_t *B = (cell_stats_t *)b;
9762 
9763     B->min = PetscMin(A->min, B->min);
9764     B->max = PetscMax(A->max, B->max);
9765     B->sum += A->sum;
9766     B->squaresum += A->squaresum;
9767     B->count += A->count;
9768   }
9769 }
9770 
9771 /*@
9772   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9773 
9774   Collective
9775 
9776   Input Parameters:
9777 + dm        - The `DMPLEX` object
9778 . output    - If true, statistics will be displayed on `stdout`
9779 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9780 
9781   Level: developer
9782 
9783   Notes:
9784   This is mainly intended for debugging/testing purposes.
9785 
9786   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9787 
9788 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9789 @*/
9790 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9791 {
9792   DM           dmCoarse;
9793   cell_stats_t stats, globalStats;
9794   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9795   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9796   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9797   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9798   PetscMPIInt  rank, size;
9799 
9800   PetscFunctionBegin;
9801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9802   stats.min = PETSC_MAX_REAL;
9803   stats.max = PETSC_MIN_REAL;
9804   stats.sum = stats.squaresum = 0.;
9805   stats.count                 = 0;
9806 
9807   PetscCallMPI(MPI_Comm_size(comm, &size));
9808   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9809   PetscCall(DMGetCoordinateDim(dm, &cdim));
9810   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9811   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9812   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9813   for (c = cStart; c < cEnd; c++) {
9814     PetscInt  i;
9815     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9816 
9817     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9818     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9819     for (i = 0; i < PetscSqr(cdim); ++i) {
9820       frobJ += J[i] * J[i];
9821       frobInvJ += invJ[i] * invJ[i];
9822     }
9823     cond2 = frobJ * frobInvJ;
9824     cond  = PetscSqrtReal(cond2);
9825 
9826     stats.min = PetscMin(stats.min, cond);
9827     stats.max = PetscMax(stats.max, cond);
9828     stats.sum += cond;
9829     stats.squaresum += cond2;
9830     stats.count++;
9831     if (output && cond > limit) {
9832       PetscSection coordSection;
9833       Vec          coordsLocal;
9834       PetscScalar *coords = NULL;
9835       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9836 
9837       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9838       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9839       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9840       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9841       for (i = 0; i < Nv / cdim; ++i) {
9842         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9843         for (d = 0; d < cdim; ++d) {
9844           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9845           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9846         }
9847         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9848       }
9849       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9850       for (cl = 0; cl < clSize * 2; cl += 2) {
9851         const PetscInt edge = closure[cl];
9852 
9853         if ((edge >= eStart) && (edge < eEnd)) {
9854           PetscReal len;
9855 
9856           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9857           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9858         }
9859       }
9860       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9861       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9862     }
9863   }
9864   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9865 
9866   if (size > 1) {
9867     PetscMPIInt  blockLengths[2] = {4, 1};
9868     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9869     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9870     MPI_Op       statReduce;
9871 
9872     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9873     PetscCallMPI(MPI_Type_commit(&statType));
9874     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9875     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9876     PetscCallMPI(MPI_Op_free(&statReduce));
9877     PetscCallMPI(MPI_Type_free(&statType));
9878   } else {
9879     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9880   }
9881   if (rank == 0) {
9882     count = globalStats.count;
9883     min   = globalStats.min;
9884     max   = globalStats.max;
9885     mean  = globalStats.sum / globalStats.count;
9886     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9887   }
9888 
9889   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));
9890   PetscCall(PetscFree2(J, invJ));
9891 
9892   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9893   if (dmCoarse) {
9894     PetscBool isplex;
9895 
9896     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9897     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9898   }
9899   PetscFunctionReturn(PETSC_SUCCESS);
9900 }
9901 
9902 /*@
9903   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9904   orthogonal quality below given tolerance.
9905 
9906   Collective
9907 
9908   Input Parameters:
9909 + dm   - The `DMPLEX` object
9910 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9911 - atol - [0, 1] Absolute tolerance for tagging cells.
9912 
9913   Output Parameters:
9914 + OrthQual      - `Vec` containing orthogonal quality per cell
9915 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9916 
9917   Options Database Keys:
9918 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9919 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9920 
9921   Level: intermediate
9922 
9923   Notes:
9924   Orthogonal quality is given by the following formula\:
9925 
9926   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9927 
9928   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
9929   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9930   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9931   calculating the cosine of the angle between these vectors.
9932 
9933   Orthogonal quality ranges from 1 (best) to 0 (worst).
9934 
9935   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9936   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9937 
9938   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9939 
9940 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9941 @*/
9942 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9943 {
9944   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9945   PetscInt              *idx;
9946   PetscScalar           *oqVals;
9947   const PetscScalar     *cellGeomArr, *faceGeomArr;
9948   PetscReal             *ci, *fi, *Ai;
9949   MPI_Comm               comm;
9950   Vec                    cellgeom, facegeom;
9951   DM                     dmFace, dmCell;
9952   IS                     glob;
9953   ISLocalToGlobalMapping ltog;
9954   PetscViewer            vwr;
9955 
9956   PetscFunctionBegin;
9957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9958   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9959   PetscAssertPointer(OrthQual, 4);
9960   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9961   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9962   PetscCall(DMGetDimension(dm, &nc));
9963   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9964   {
9965     DMPlexInterpolatedFlag interpFlag;
9966 
9967     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9968     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9969       PetscMPIInt rank;
9970 
9971       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9972       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9973     }
9974   }
9975   if (OrthQualLabel) {
9976     PetscAssertPointer(OrthQualLabel, 5);
9977     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9978     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9979   } else {
9980     *OrthQualLabel = NULL;
9981   }
9982   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9983   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9984   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9985   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9986   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9987   PetscCall(VecCreate(comm, OrthQual));
9988   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9989   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9990   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9991   PetscCall(VecSetUp(*OrthQual));
9992   PetscCall(ISDestroy(&glob));
9993   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9994   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9995   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9996   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9997   PetscCall(VecGetDM(cellgeom, &dmCell));
9998   PetscCall(VecGetDM(facegeom, &dmFace));
9999   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10000   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10001     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10002     PetscInt         cellarr[2], *adj = NULL;
10003     PetscScalar     *cArr, *fArr;
10004     PetscReal        minvalc = 1.0, minvalf = 1.0;
10005     PetscFVCellGeom *cg;
10006 
10007     idx[cellIter] = cell - cStart;
10008     cellarr[0]    = cell;
10009     /* Make indexing into cellGeom easier */
10010     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10011     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10012     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10013     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10014     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10015       PetscInt         i;
10016       const PetscInt   neigh  = adj[cellneigh];
10017       PetscReal        normci = 0, normfi = 0, normai = 0;
10018       PetscFVCellGeom *cgneigh;
10019       PetscFVFaceGeom *fg;
10020 
10021       /* Don't count ourselves in the neighbor list */
10022       if (neigh == cell) continue;
10023       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10024       cellarr[1] = neigh;
10025       {
10026         PetscInt        numcovpts;
10027         const PetscInt *covpts;
10028 
10029         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10030         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10031         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10032       }
10033 
10034       /* Compute c_i, f_i and their norms */
10035       for (i = 0; i < nc; i++) {
10036         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10037         fi[i] = fg->centroid[i] - cg->centroid[i];
10038         Ai[i] = fg->normal[i];
10039         normci += PetscPowReal(ci[i], 2);
10040         normfi += PetscPowReal(fi[i], 2);
10041         normai += PetscPowReal(Ai[i], 2);
10042       }
10043       normci = PetscSqrtReal(normci);
10044       normfi = PetscSqrtReal(normfi);
10045       normai = PetscSqrtReal(normai);
10046 
10047       /* Normalize and compute for each face-cell-normal pair */
10048       for (i = 0; i < nc; i++) {
10049         ci[i] = ci[i] / normci;
10050         fi[i] = fi[i] / normfi;
10051         Ai[i] = Ai[i] / normai;
10052         /* PetscAbs because I don't know if normals are guaranteed to point out */
10053         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10054         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10055       }
10056       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10057       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10058     }
10059     PetscCall(PetscFree(adj));
10060     PetscCall(PetscFree2(cArr, fArr));
10061     /* Defer to cell if they're equal */
10062     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10063     if (OrthQualLabel) {
10064       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10065     }
10066   }
10067   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10068   PetscCall(VecAssemblyBegin(*OrthQual));
10069   PetscCall(VecAssemblyEnd(*OrthQual));
10070   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10071   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10072   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10073   if (OrthQualLabel) {
10074     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10075   }
10076   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10077   PetscCall(PetscViewerDestroy(&vwr));
10078   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10079   PetscFunctionReturn(PETSC_SUCCESS);
10080 }
10081 
10082 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10083  * interpolator construction */
10084 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10085 {
10086   PetscSection section, newSection, gsection;
10087   PetscSF      sf;
10088   PetscBool    hasConstraints, ghasConstraints;
10089 
10090   PetscFunctionBegin;
10091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10092   PetscAssertPointer(odm, 2);
10093   PetscCall(DMGetLocalSection(dm, &section));
10094   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10095   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10096   if (!ghasConstraints) {
10097     PetscCall(PetscObjectReference((PetscObject)dm));
10098     *odm = dm;
10099     PetscFunctionReturn(PETSC_SUCCESS);
10100   }
10101   PetscCall(DMClone(dm, odm));
10102   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10103   PetscCall(DMGetLocalSection(*odm, &newSection));
10104   PetscCall(DMGetPointSF(*odm, &sf));
10105   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10106   PetscCall(DMSetGlobalSection(*odm, gsection));
10107   PetscCall(PetscSectionDestroy(&gsection));
10108   PetscFunctionReturn(PETSC_SUCCESS);
10109 }
10110 
10111 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10112 {
10113   DM        dmco, dmfo;
10114   Mat       interpo;
10115   Vec       rscale;
10116   Vec       cglobalo, clocal;
10117   Vec       fglobal, fglobalo, flocal;
10118   PetscBool regular;
10119 
10120   PetscFunctionBegin;
10121   PetscCall(DMGetFullDM(dmc, &dmco));
10122   PetscCall(DMGetFullDM(dmf, &dmfo));
10123   PetscCall(DMSetCoarseDM(dmfo, dmco));
10124   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10125   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10126   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10127   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10128   PetscCall(DMCreateLocalVector(dmc, &clocal));
10129   PetscCall(VecSet(cglobalo, 0.));
10130   PetscCall(VecSet(clocal, 0.));
10131   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10132   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10133   PetscCall(DMCreateLocalVector(dmf, &flocal));
10134   PetscCall(VecSet(fglobal, 0.));
10135   PetscCall(VecSet(fglobalo, 0.));
10136   PetscCall(VecSet(flocal, 0.));
10137   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10138   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10139   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10140   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10141   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10142   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10143   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10144   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10145   *shift = fglobal;
10146   PetscCall(VecDestroy(&flocal));
10147   PetscCall(VecDestroy(&fglobalo));
10148   PetscCall(VecDestroy(&clocal));
10149   PetscCall(VecDestroy(&cglobalo));
10150   PetscCall(VecDestroy(&rscale));
10151   PetscCall(MatDestroy(&interpo));
10152   PetscCall(DMDestroy(&dmfo));
10153   PetscCall(DMDestroy(&dmco));
10154   PetscFunctionReturn(PETSC_SUCCESS);
10155 }
10156 
10157 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10158 {
10159   PetscObject shifto;
10160   Vec         shift;
10161 
10162   PetscFunctionBegin;
10163   if (!interp) {
10164     Vec rscale;
10165 
10166     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10167     PetscCall(VecDestroy(&rscale));
10168   } else {
10169     PetscCall(PetscObjectReference((PetscObject)interp));
10170   }
10171   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10172   if (!shifto) {
10173     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10174     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10175     shifto = (PetscObject)shift;
10176     PetscCall(VecDestroy(&shift));
10177   }
10178   shift = (Vec)shifto;
10179   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10180   PetscCall(VecAXPY(fineSol, 1.0, shift));
10181   PetscCall(MatDestroy(&interp));
10182   PetscFunctionReturn(PETSC_SUCCESS);
10183 }
10184 
10185 /* Pointwise interpolation
10186      Just code FEM for now
10187      u^f = I u^c
10188      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10189      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10190      I_{ij} = psi^f_i phi^c_j
10191 */
10192 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10193 {
10194   PetscSection gsc, gsf;
10195   PetscInt     m, n;
10196   void        *ctx;
10197   DM           cdm;
10198   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10199 
10200   PetscFunctionBegin;
10201   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10202   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10203   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10204   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10205 
10206   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10207   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10208   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10209   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10210   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10211 
10212   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10213   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10214   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10215   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10216   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10217   if (scaling) {
10218     /* Use naive scaling */
10219     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10220   }
10221   PetscFunctionReturn(PETSC_SUCCESS);
10222 }
10223 
10224 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10225 {
10226   VecScatter ctx;
10227 
10228   PetscFunctionBegin;
10229   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10230   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10231   PetscCall(VecScatterDestroy(&ctx));
10232   PetscFunctionReturn(PETSC_SUCCESS);
10233 }
10234 
10235 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[])
10236 {
10237   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10238   const PetscInt Nc = uOff[f + 1] - uOff[f];
10239   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10240 }
10241 
10242 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10243 {
10244   DM           dmc;
10245   PetscDS      ds;
10246   Vec          ones, locmass;
10247   IS           cellIS;
10248   PetscFormKey key;
10249   PetscInt     depth;
10250 
10251   PetscFunctionBegin;
10252   PetscCall(DMClone(dm, &dmc));
10253   PetscCall(DMCopyDisc(dm, dmc));
10254   PetscCall(DMGetDS(dmc, &ds));
10255   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10256   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10257   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10258   else PetscCall(DMGetLocalVector(dm, &locmass));
10259   PetscCall(DMGetLocalVector(dm, &ones));
10260   PetscCall(DMPlexGetDepth(dm, &depth));
10261   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10262   PetscCall(VecSet(locmass, 0.0));
10263   PetscCall(VecSet(ones, 1.0));
10264   key.label = NULL;
10265   key.value = 0;
10266   key.field = 0;
10267   key.part  = 0;
10268   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10269   PetscCall(ISDestroy(&cellIS));
10270   if (mass) {
10271     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10272     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10273   }
10274   PetscCall(DMRestoreLocalVector(dm, &ones));
10275   if (lmass) *lmass = locmass;
10276   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10277   PetscCall(DMDestroy(&dmc));
10278   PetscFunctionReturn(PETSC_SUCCESS);
10279 }
10280 
10281 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10282 {
10283   PetscSection gsc, gsf;
10284   PetscInt     m, n;
10285   void        *ctx;
10286   DM           cdm;
10287   PetscBool    regular;
10288 
10289   PetscFunctionBegin;
10290   if (dmFine == dmCoarse) {
10291     DM            dmc;
10292     PetscDS       ds;
10293     PetscWeakForm wf;
10294     Vec           u;
10295     IS            cellIS;
10296     PetscFormKey  key;
10297     PetscInt      depth;
10298 
10299     PetscCall(DMClone(dmFine, &dmc));
10300     PetscCall(DMCopyDisc(dmFine, dmc));
10301     PetscCall(DMGetDS(dmc, &ds));
10302     PetscCall(PetscDSGetWeakForm(ds, &wf));
10303     PetscCall(PetscWeakFormClear(wf));
10304     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10305     PetscCall(DMCreateMatrix(dmc, mass));
10306     PetscCall(DMGetLocalVector(dmc, &u));
10307     PetscCall(DMPlexGetDepth(dmc, &depth));
10308     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10309     PetscCall(MatZeroEntries(*mass));
10310     key.label = NULL;
10311     key.value = 0;
10312     key.field = 0;
10313     key.part  = 0;
10314     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10315     PetscCall(ISDestroy(&cellIS));
10316     PetscCall(DMRestoreLocalVector(dmc, &u));
10317     PetscCall(DMDestroy(&dmc));
10318   } else {
10319     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10320     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10321     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10322     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10323 
10324     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10325     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10326     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10327     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10328 
10329     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10330     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10331     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10332     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10333   }
10334   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10335   PetscFunctionReturn(PETSC_SUCCESS);
10336 }
10337 
10338 /*@
10339   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10340 
10341   Input Parameter:
10342 . dm - The `DMPLEX` object
10343 
10344   Output Parameter:
10345 . regular - The flag
10346 
10347   Level: intermediate
10348 
10349 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10350 @*/
10351 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10352 {
10353   PetscFunctionBegin;
10354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10355   PetscAssertPointer(regular, 2);
10356   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10357   PetscFunctionReturn(PETSC_SUCCESS);
10358 }
10359 
10360 /*@
10361   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10362 
10363   Input Parameters:
10364 + dm      - The `DMPLEX` object
10365 - regular - The flag
10366 
10367   Level: intermediate
10368 
10369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10370 @*/
10371 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10372 {
10373   PetscFunctionBegin;
10374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10375   ((DM_Plex *)dm->data)->regularRefinement = regular;
10376   PetscFunctionReturn(PETSC_SUCCESS);
10377 }
10378 
10379 /*@
10380   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10381   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10382 
10383   Not Collective
10384 
10385   Input Parameter:
10386 . dm - The `DMPLEX` object
10387 
10388   Output Parameters:
10389 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10390 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10391 
10392   Level: intermediate
10393 
10394 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10395 @*/
10396 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10397 {
10398   DM_Plex *plex = (DM_Plex *)dm->data;
10399 
10400   PetscFunctionBegin;
10401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10402   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10403   if (anchorSection) *anchorSection = plex->anchorSection;
10404   if (anchorIS) *anchorIS = plex->anchorIS;
10405   PetscFunctionReturn(PETSC_SUCCESS);
10406 }
10407 
10408 /*@
10409   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10410 
10411   Collective
10412 
10413   Input Parameters:
10414 + dm            - The `DMPLEX` object
10415 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10416                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10417 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10418 
10419   Level: intermediate
10420 
10421   Notes:
10422   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10423   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10424   combination of other points' degrees of freedom.
10425 
10426   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10427   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10428 
10429   The reference counts of `anchorSection` and `anchorIS` are incremented.
10430 
10431 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10432 @*/
10433 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10434 {
10435   DM_Plex    *plex = (DM_Plex *)dm->data;
10436   PetscMPIInt result;
10437 
10438   PetscFunctionBegin;
10439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10440   if (anchorSection) {
10441     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10442     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10443     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10444   }
10445   if (anchorIS) {
10446     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10447     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10448     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10449   }
10450 
10451   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10452   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10453   plex->anchorSection = anchorSection;
10454 
10455   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10456   PetscCall(ISDestroy(&plex->anchorIS));
10457   plex->anchorIS = anchorIS;
10458 
10459   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10460     PetscInt        size, a, pStart, pEnd;
10461     const PetscInt *anchors;
10462 
10463     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10464     PetscCall(ISGetLocalSize(anchorIS, &size));
10465     PetscCall(ISGetIndices(anchorIS, &anchors));
10466     for (a = 0; a < size; a++) {
10467       PetscInt p;
10468 
10469       p = anchors[a];
10470       if (p >= pStart && p < pEnd) {
10471         PetscInt dof;
10472 
10473         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10474         if (dof) {
10475           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10476           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10477         }
10478       }
10479     }
10480     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10481   }
10482   /* reset the generic constraints */
10483   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10484   PetscFunctionReturn(PETSC_SUCCESS);
10485 }
10486 
10487 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10488 {
10489   PetscSection anchorSection;
10490   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10491 
10492   PetscFunctionBegin;
10493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10494   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10495   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10496   PetscCall(PetscSectionGetNumFields(section, &numFields));
10497   if (numFields) {
10498     PetscInt f;
10499     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10500 
10501     for (f = 0; f < numFields; f++) {
10502       PetscInt numComp;
10503 
10504       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10505       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10506     }
10507   }
10508   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10509   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10510   pStart = PetscMax(pStart, sStart);
10511   pEnd   = PetscMin(pEnd, sEnd);
10512   pEnd   = PetscMax(pStart, pEnd);
10513   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10514   for (p = pStart; p < pEnd; p++) {
10515     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10516     if (dof) {
10517       PetscCall(PetscSectionGetDof(section, p, &dof));
10518       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10519       for (f = 0; f < numFields; f++) {
10520         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10521         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10522       }
10523     }
10524   }
10525   PetscCall(PetscSectionSetUp(*cSec));
10526   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10527   PetscFunctionReturn(PETSC_SUCCESS);
10528 }
10529 
10530 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10531 {
10532   PetscSection    aSec;
10533   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10534   const PetscInt *anchors;
10535   PetscInt        numFields, f;
10536   IS              aIS;
10537   MatType         mtype;
10538   PetscBool       iscuda, iskokkos;
10539 
10540   PetscFunctionBegin;
10541   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10542   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10543   PetscCall(PetscSectionGetStorageSize(section, &n));
10544   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10545   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10546   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10547   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10548   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10549   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10550   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10551   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10552   else mtype = MATSEQAIJ;
10553   PetscCall(MatSetType(*cMat, mtype));
10554   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10555   PetscCall(ISGetIndices(aIS, &anchors));
10556   /* cSec will be a subset of aSec and section */
10557   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10558   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10559   PetscCall(PetscMalloc1(m + 1, &i));
10560   i[0] = 0;
10561   PetscCall(PetscSectionGetNumFields(section, &numFields));
10562   for (p = pStart; p < pEnd; p++) {
10563     PetscInt rDof, rOff, r;
10564 
10565     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10566     if (!rDof) continue;
10567     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10568     if (numFields) {
10569       for (f = 0; f < numFields; f++) {
10570         annz = 0;
10571         for (r = 0; r < rDof; r++) {
10572           a = anchors[rOff + r];
10573           if (a < sStart || a >= sEnd) continue;
10574           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10575           annz += aDof;
10576         }
10577         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10578         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10579         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10580       }
10581     } else {
10582       annz = 0;
10583       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10584       for (q = 0; q < dof; q++) {
10585         a = anchors[rOff + q];
10586         if (a < sStart || a >= sEnd) continue;
10587         PetscCall(PetscSectionGetDof(section, a, &aDof));
10588         annz += aDof;
10589       }
10590       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10591       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10592       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10593     }
10594   }
10595   nnz = i[m];
10596   PetscCall(PetscMalloc1(nnz, &j));
10597   offset = 0;
10598   for (p = pStart; p < pEnd; p++) {
10599     if (numFields) {
10600       for (f = 0; f < numFields; f++) {
10601         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10602         for (q = 0; q < dof; q++) {
10603           PetscInt rDof, rOff, r;
10604           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10605           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10606           for (r = 0; r < rDof; r++) {
10607             PetscInt s;
10608 
10609             a = anchors[rOff + r];
10610             if (a < sStart || a >= sEnd) continue;
10611             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10612             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10613             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10614           }
10615         }
10616       }
10617     } else {
10618       PetscCall(PetscSectionGetDof(cSec, p, &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(PetscSectionGetDof(section, a, &aDof));
10629           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10630           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10631         }
10632       }
10633     }
10634   }
10635   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10636   PetscCall(PetscFree(i));
10637   PetscCall(PetscFree(j));
10638   PetscCall(ISRestoreIndices(aIS, &anchors));
10639   PetscFunctionReturn(PETSC_SUCCESS);
10640 }
10641 
10642 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10643 {
10644   DM_Plex     *plex = (DM_Plex *)dm->data;
10645   PetscSection anchorSection, section, cSec;
10646   Mat          cMat;
10647 
10648   PetscFunctionBegin;
10649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10650   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10651   if (anchorSection) {
10652     PetscInt Nf;
10653 
10654     PetscCall(DMGetLocalSection(dm, &section));
10655     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10656     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10657     PetscCall(DMGetNumFields(dm, &Nf));
10658     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10659     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10660     PetscCall(PetscSectionDestroy(&cSec));
10661     PetscCall(MatDestroy(&cMat));
10662   }
10663   PetscFunctionReturn(PETSC_SUCCESS);
10664 }
10665 
10666 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10667 {
10668   IS           subis;
10669   PetscSection section, subsection;
10670 
10671   PetscFunctionBegin;
10672   PetscCall(DMGetLocalSection(dm, &section));
10673   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10674   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10675   /* Create subdomain */
10676   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10677   /* Create submodel */
10678   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10679   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10680   PetscCall(DMSetLocalSection(*subdm, subsection));
10681   PetscCall(PetscSectionDestroy(&subsection));
10682   PetscCall(DMCopyDisc(dm, *subdm));
10683   /* Create map from submodel to global model */
10684   if (is) {
10685     PetscSection    sectionGlobal, subsectionGlobal;
10686     IS              spIS;
10687     const PetscInt *spmap;
10688     PetscInt       *subIndices;
10689     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10690     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10691 
10692     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10693     PetscCall(ISGetIndices(spIS, &spmap));
10694     PetscCall(PetscSectionGetNumFields(section, &Nf));
10695     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10696     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10697     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10698     for (p = pStart; p < pEnd; ++p) {
10699       PetscInt gdof, pSubSize = 0;
10700 
10701       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10702       if (gdof > 0) {
10703         for (f = 0; f < Nf; ++f) {
10704           PetscInt fdof, fcdof;
10705 
10706           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10707           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10708           pSubSize += fdof - fcdof;
10709         }
10710         subSize += pSubSize;
10711         if (pSubSize) {
10712           if (bs < 0) {
10713             bs = pSubSize;
10714           } else if (bs != pSubSize) {
10715             /* Layout does not admit a pointwise block size */
10716             bs = 1;
10717           }
10718         }
10719       }
10720     }
10721     /* Must have same blocksize on all procs (some might have no points) */
10722     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10723     bsLocal[1] = bs;
10724     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10725     if (bsMinMax[0] != bsMinMax[1]) {
10726       bs = 1;
10727     } else {
10728       bs = bsMinMax[0];
10729     }
10730     PetscCall(PetscMalloc1(subSize, &subIndices));
10731     for (p = pStart; p < pEnd; ++p) {
10732       PetscInt gdof, goff;
10733 
10734       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10735       if (gdof > 0) {
10736         const PetscInt point = spmap[p];
10737 
10738         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10739         for (f = 0; f < Nf; ++f) {
10740           PetscInt fdof, fcdof, fc, f2, poff = 0;
10741 
10742           /* Can get rid of this loop by storing field information in the global section */
10743           for (f2 = 0; f2 < f; ++f2) {
10744             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10745             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10746             poff += fdof - fcdof;
10747           }
10748           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10749           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10750           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10751         }
10752       }
10753     }
10754     PetscCall(ISRestoreIndices(spIS, &spmap));
10755     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10756     if (bs > 1) {
10757       /* We need to check that the block size does not come from non-contiguous fields */
10758       PetscInt i, j, set = 1;
10759       for (i = 0; i < subSize; i += bs) {
10760         for (j = 0; j < bs; ++j) {
10761           if (subIndices[i + j] != subIndices[i] + j) {
10762             set = 0;
10763             break;
10764           }
10765         }
10766       }
10767       if (set) PetscCall(ISSetBlockSize(*is, bs));
10768     }
10769     /* Attach nullspace */
10770     for (f = 0; f < Nf; ++f) {
10771       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10772       if ((*subdm)->nullspaceConstructors[f]) break;
10773     }
10774     if (f < Nf) {
10775       MatNullSpace nullSpace;
10776       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10777 
10778       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10779       PetscCall(MatNullSpaceDestroy(&nullSpace));
10780     }
10781   }
10782   PetscFunctionReturn(PETSC_SUCCESS);
10783 }
10784 
10785 /*@
10786   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10787 
10788   Input Parameters:
10789 + dm    - The `DM`
10790 - dummy - unused argument
10791 
10792   Options Database Key:
10793 . -dm_plex_monitor_throughput - Activate the monitor
10794 
10795   Level: developer
10796 
10797 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10798 @*/
10799 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10800 {
10801   PetscLogHandler default_handler;
10802 
10803   PetscFunctionBegin;
10804   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10805   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10806   if (default_handler) {
10807     PetscLogEvent      event;
10808     PetscEventPerfInfo eventInfo;
10809     PetscLogDouble     cellRate, flopRate;
10810     PetscInt           cStart, cEnd, Nf, N;
10811     const char        *name;
10812 
10813     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10814     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10815     PetscCall(DMGetNumFields(dm, &Nf));
10816     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10817     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10818     N        = (cEnd - cStart) * Nf * eventInfo.count;
10819     flopRate = eventInfo.flops / eventInfo.time;
10820     cellRate = N / eventInfo.time;
10821     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));
10822   } else {
10823     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.");
10824   }
10825   PetscFunctionReturn(PETSC_SUCCESS);
10826 }
10827