xref: /petsc/src/dm/impls/plex/plex.c (revision 3f02e49b19195914bf17f317a25cb39636853415)
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_DistributeMultistage, 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_GetLocalOffsets, DMPLEX_Uninterpolate;
16 PetscLogEvent DMPLEX_DistributionView, DMPLEX_DistributionLoad;
17 
18 PetscBool  Plexcite       = PETSC_FALSE;
19 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
20                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
21                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
22                             "journal   = {SIAM Journal on Scientific Computing},\n"
23                             "volume    = {38},\n"
24                             "number    = {5},\n"
25                             "pages     = {S143--S155},\n"
26                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
27                             "doi       = {10.1137/15M1026092},\n"
28                             "year      = {2016},\n"
29                             "petsc_uses={DMPlex},\n}\n";
30 
31 PETSC_SINGLE_LIBRARY_INTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
32 
33 /*@
34   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
35 
36   Input Parameter:
37 . dm - The `DMPLEX` object
38 
39   Output Parameter:
40 . simplex - Flag checking for a simplex
41 
42   Level: intermediate
43 
44   Note:
45   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
46   If the mesh has no cells, this returns `PETSC_FALSE`.
47 
48 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
49 @*/
50 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
51 {
52   DMPolytopeType ct;
53   PetscInt       cStart, cEnd;
54 
55   PetscFunctionBegin;
56   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
57   if (cEnd <= cStart) {
58     *simplex = PETSC_FALSE;
59     PetscFunctionReturn(PETSC_SUCCESS);
60   }
61   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
62   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
63   PetscFunctionReturn(PETSC_SUCCESS);
64 }
65 
66 /*@
67   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
68 
69   Input Parameters:
70 + dm     - The `DMPLEX` object
71 - height - The cell height in the Plex, 0 is the default
72 
73   Output Parameters:
74 + cStart - The first "normal" cell, pass `NULL` if not needed
75 - cEnd   - The upper bound on "normal" cells, pass `NULL` if not needed
76 
77   Level: developer
78 
79   Note:
80   This function requires that tensor cells are ordered last.
81 
82 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
83 @*/
84 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PeOp PetscInt *cStart, PeOp PetscInt *cEnd)
85 {
86   DMLabel         ctLabel;
87   IS              valueIS;
88   const PetscInt *ctypes;
89   PetscBool       found = PETSC_FALSE;
90   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
91 
92   PetscFunctionBegin;
93   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
94   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
95   PetscCall(ISGetLocalSize(valueIS, &Nct));
96   PetscCall(ISGetIndices(valueIS, &ctypes));
97   for (PetscInt t = 0; t < Nct; ++t) {
98     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
99     PetscInt             ctS, ctE, ht;
100 
101     if (ct == DM_POLYTOPE_UNKNOWN) {
102       // If any cells are not typed, just use all cells
103       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
104       break;
105     }
106     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
107     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
108     if (ctS >= ctE) continue;
109     // Check that a point has the right height
110     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
111     if (ht != height) continue;
112     cS    = PetscMin(cS, ctS);
113     cE    = PetscMax(cE, ctE);
114     found = PETSC_TRUE;
115   }
116   if (!Nct || !found) cS = cE = 0;
117   PetscCall(ISDestroy(&valueIS));
118   // Reset label for fast lookup
119   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
120   if (cStart) *cStart = cS;
121   if (cEnd) *cEnd = cE;
122   PetscFunctionReturn(PETSC_SUCCESS);
123 }
124 
125 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
126 {
127   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
128   PetscInt                *sStart, *sEnd;
129   PetscViewerVTKFieldType *ft;
130   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
131   DMLabel                  depthLabel, ctLabel;
132 
133   PetscFunctionBegin;
134   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
135   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
136   PetscCall(DMGetCoordinateDim(dm, &cdim));
137   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
138   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
139   if (field >= 0) {
140     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
141   } else {
142     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
143   }
144 
145   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
146   PetscCall(DMPlexGetDepth(dm, &depth));
147   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
148   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
149   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
150     const DMPolytopeType ict = (DMPolytopeType)c;
151     PetscInt             dep;
152 
153     if (ict == DM_POLYTOPE_FV_GHOST) continue;
154     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
155     if (pStart >= 0) {
156       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
157       if (dep != depth - cellHeight) continue;
158     }
159     if (field >= 0) {
160       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
161     } else {
162       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
163     }
164   }
165 
166   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
167   *types = 0;
168 
169   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
170     if (globalvcdof[c]) ++(*types);
171   }
172 
173   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
174   t = 0;
175   if (globalvcdof[DM_NUM_POLYTOPES]) {
176     sStart[t] = vStart;
177     sEnd[t]   = vEnd;
178     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
179     ++t;
180   }
181 
182   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
183     if (globalvcdof[c]) {
184       const DMPolytopeType ict = (DMPolytopeType)c;
185 
186       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
187       sStart[t] = cStart;
188       sEnd[t]   = cEnd;
189       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
190       ++t;
191     }
192   }
193 
194   if (!*types) {
195     if (field >= 0) {
196       const char *fieldname;
197 
198       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
199       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
200     } else {
201       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
202     }
203   }
204 
205   *ssStart = sStart;
206   *ssEnd   = sEnd;
207   *sft     = ft;
208   PetscFunctionReturn(PETSC_SUCCESS);
209 }
210 
211 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
212 {
213   PetscFunctionBegin;
214   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
215   PetscFunctionReturn(PETSC_SUCCESS);
216 }
217 
218 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
219 {
220   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
221   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
222 
223   PetscFunctionBegin;
224   *ft = PETSC_VTK_INVALID;
225   PetscCall(DMGetCoordinateDim(dm, &cdim));
226   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
227   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
228   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
229   if (field >= 0) {
230     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
231     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
232   } else {
233     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
234     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
235   }
236   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
237   if (globalvcdof[0]) {
238     *sStart = vStart;
239     *sEnd   = vEnd;
240     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
241     else *ft = PETSC_VTK_POINT_FIELD;
242   } else if (globalvcdof[1]) {
243     *sStart = cStart;
244     *sEnd   = cEnd;
245     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
246     else *ft = PETSC_VTK_CELL_FIELD;
247   } else {
248     if (field >= 0) {
249       const char *fieldname;
250 
251       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
252       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
253     } else {
254       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section\n"));
255     }
256   }
257   PetscFunctionReturn(PETSC_SUCCESS);
258 }
259 
260 /*@
261   DMPlexVecView1D - Plot many 1D solutions on the same line graph
262 
263   Collective
264 
265   Input Parameters:
266 + dm     - The `DMPLEX` object
267 . n      - The number of vectors
268 . u      - The array of local vectors
269 - viewer - The `PetscViewer`
270 
271   Level: advanced
272 
273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
274 @*/
275 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
276 {
277   DM                 cdm;
278   PetscDS            ds;
279   PetscDraw          draw = NULL;
280   PetscDrawLG        lg;
281   Vec                coordinates;
282   const PetscScalar *coords, **sol;
283   PetscReal         *vals;
284   PetscInt          *Nc;
285   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
286   char             **names;
287 
288   PetscFunctionBegin;
289   PetscCall(DMGetCoordinateDM(dm, &cdm));
290   PetscCall(DMGetDS(dm, &ds));
291   PetscCall(PetscDSGetNumFields(ds, &Nf));
292   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
293   PetscCall(PetscDSGetComponents(ds, &Nc));
294 
295   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
296   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
297   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
298 
299   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
300   for (PetscInt i = 0, l = 0; i < n; ++i) {
301     const char *vname;
302 
303     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
304     for (PetscInt f = 0; f < Nf; ++f) {
305       PetscObject disc;
306       const char *fname;
307       char        tmpname[PETSC_MAX_PATH_LEN];
308 
309       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
310       /* TODO Create names for components */
311       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
312         PetscCall(PetscObjectGetName(disc, &fname));
313         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
314         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
315         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
316         PetscCall(PetscStrallocpy(tmpname, &names[l]));
317       }
318     }
319   }
320   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
321   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
322   PetscCall(VecGetArrayRead(coordinates, &coords));
323   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
324   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
325   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
326   PetscSection s;
327   PetscInt     cdof, vdof;
328 
329   PetscCall(DMGetLocalSection(dm, &s));
330   PetscCall(PetscSectionGetDof(s, eStart, &cdof));
331   PetscCall(PetscSectionGetDof(s, vStart, &vdof));
332   if (cdof) {
333     if (vdof) {
334       // P_2
335       PetscInt vFirst = -1;
336 
337       for (PetscInt e = eStart; e < eEnd; ++e) {
338         PetscScalar    *xa, *xb, *svals;
339         const PetscInt *cone;
340 
341         PetscCall(DMPlexGetCone(dm, e, &cone));
342         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
343         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
344         if (e == eStart) vFirst = cone[0];
345         for (PetscInt i = 0; i < n; ++i) {
346           PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
347           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
348         }
349         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
350         if (e == eEnd - 1 && cone[1] != vFirst) {
351           for (PetscInt i = 0; i < n; ++i) {
352             PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
353             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
354           }
355           PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
356           for (PetscInt i = 0; i < n; ++i) {
357             PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
358             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
359           }
360           PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
361         }
362       }
363     } else {
364       // P_0
365       for (PetscInt e = eStart; e < eEnd; ++e) {
366         PetscScalar    *xa, *xb, *svals;
367         const PetscInt *cone;
368 
369         PetscCall(DMPlexGetCone(dm, e, &cone));
370         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
371         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
372         for (PetscInt i = 0; i < n; ++i) {
373           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
374           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
375         }
376         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
377       }
378     }
379   } else if (vdof) {
380     // P_1
381     for (PetscInt v = vStart; v < vEnd; ++v) {
382       PetscScalar *x, *svals;
383 
384       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
385       for (PetscInt i = 0; i < n; ++i) {
386         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
387         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
388       }
389       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
390     }
391   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Discretization not supported");
392   PetscCall(VecRestoreArrayRead(coordinates, &coords));
393   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
394   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
395   PetscCall(PetscFree3(sol, names, vals));
396 
397   PetscCall(PetscDrawLGDraw(lg));
398   PetscCall(PetscDrawLGDestroy(&lg));
399   PetscFunctionReturn(PETSC_SUCCESS);
400 }
401 
402 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
403 {
404   DM dm;
405 
406   PetscFunctionBegin;
407   PetscCall(VecGetDM(u, &dm));
408   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
409   PetscFunctionReturn(PETSC_SUCCESS);
410 }
411 
412 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
413 {
414   DM                 dm;
415   PetscSection       s;
416   PetscDraw          draw, popup;
417   DM                 cdm;
418   PetscSection       coordSection;
419   Vec                coordinates;
420   const PetscScalar *array;
421   PetscReal          lbound[3], ubound[3];
422   PetscReal          vbound[2], time;
423   PetscBool          flg;
424   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
425   const char        *name;
426   char               title[PETSC_MAX_PATH_LEN];
427 
428   PetscFunctionBegin;
429   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
430   PetscCall(VecGetDM(v, &dm));
431   PetscCall(DMGetCoordinateDim(dm, &dim));
432   PetscCall(DMGetLocalSection(dm, &s));
433   PetscCall(PetscSectionGetNumFields(s, &Nf));
434   PetscCall(DMGetCoarsenLevel(dm, &level));
435   PetscCall(DMGetCoordinateDM(dm, &cdm));
436   PetscCall(DMGetLocalSection(cdm, &coordSection));
437   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
438   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
439   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
440 
441   PetscCall(PetscObjectGetName((PetscObject)v, &name));
442   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
443 
444   PetscCall(VecGetLocalSize(coordinates, &N));
445   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
446   PetscCall(PetscDrawClear(draw));
447 
448   /* Could implement something like DMDASelectFields() */
449   for (f = 0; f < Nf; ++f) {
450     DM          fdm = dm;
451     Vec         fv  = v;
452     IS          fis;
453     char        prefix[PETSC_MAX_PATH_LEN];
454     const char *fname;
455 
456     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
457     PetscCall(PetscSectionGetFieldName(s, f, &fname));
458 
459     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
460     else prefix[0] = '\0';
461     if (Nf > 1) {
462       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
463       PetscCall(VecGetSubVector(v, fis, &fv));
464       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
465       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
466     }
467     for (comp = 0; comp < Nc; ++comp, ++w) {
468       PetscInt nmax = 2;
469 
470       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
471       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
472       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
473       PetscCall(PetscDrawSetTitle(draw, title));
474 
475       /* TODO Get max and min only for this component */
476       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
477       if (!flg) {
478         PetscCall(VecMin(fv, NULL, &vbound[0]));
479         PetscCall(VecMax(fv, NULL, &vbound[1]));
480         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
481       }
482 
483       PetscCall(PetscDrawGetPopup(draw, &popup));
484       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
485       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
486       PetscCall(VecGetArrayRead(fv, &array));
487       for (c = cStart; c < cEnd; ++c) {
488         DMPolytopeType     ct;
489         PetscScalar       *coords = NULL, *a = NULL;
490         const PetscScalar *coords_arr;
491         PetscBool          isDG;
492         PetscInt           numCoords;
493         int                color[4] = {-1, -1, -1, -1};
494 
495         PetscCall(DMPlexGetCellType(dm, c, &ct));
496         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
497         if (a) {
498           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
499           color[1] = color[2] = color[3] = color[0];
500         } else {
501           PetscScalar *vals = NULL;
502           PetscInt     numVals, va;
503 
504           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
505           if (!numVals) {
506             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
507             continue;
508           }
509           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);
510           switch (numVals / Nc) {
511           case 1: /* P1 Clamped Segment Prism */
512           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
513             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]);
514             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
515             break;
516           case 3: /* P1 Triangle */
517           case 4: /* P1 Quadrangle */
518             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]);
519             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
520             break;
521           case 6: /* P2 Triangle */
522           case 8: /* P2 Quadrangle */
523             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]);
524             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
525             break;
526           default:
527             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
528           }
529           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
530         }
531         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
532         switch (numCoords) {
533         case 6:
534         case 12: /* Localized triangle */
535           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]));
536           break;
537         case 8:
538         case 16: /* Localized quadrilateral */
539           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
540             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
541           } else {
542             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]));
543             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]));
544           }
545           break;
546         default:
547           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
548         }
549         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
550       }
551       PetscCall(VecRestoreArrayRead(fv, &array));
552       PetscCall(PetscDrawFlush(draw));
553       PetscCall(PetscDrawPause(draw));
554       PetscCall(PetscDrawSave(draw));
555     }
556     if (Nf > 1) {
557       PetscCall(VecRestoreSubVector(v, fis, &fv));
558       PetscCall(ISDestroy(&fis));
559       PetscCall(DMDestroy(&fdm));
560     }
561   }
562   PetscFunctionReturn(PETSC_SUCCESS);
563 }
564 
565 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
566 {
567   DM        dm;
568   PetscDraw draw;
569   PetscInt  dim;
570   PetscBool isnull;
571 
572   PetscFunctionBegin;
573   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
574   PetscCall(PetscDrawIsNull(draw, &isnull));
575   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
576 
577   PetscCall(VecGetDM(v, &dm));
578   PetscCall(DMGetCoordinateDim(dm, &dim));
579   switch (dim) {
580   case 1:
581     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
582     break;
583   case 2:
584     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
585     break;
586   default:
587     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
588   }
589   PetscFunctionReturn(PETSC_SUCCESS);
590 }
591 
592 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
593 {
594   DM                      dm;
595   Vec                     locv;
596   const char             *name;
597   PetscSection            section;
598   PetscInt                pStart, pEnd;
599   PetscInt                numFields;
600   PetscViewerVTKFieldType ft;
601 
602   PetscFunctionBegin;
603   PetscCall(VecGetDM(v, &dm));
604   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
605   PetscCall(PetscObjectGetName((PetscObject)v, &name));
606   PetscCall(PetscObjectSetName((PetscObject)locv, name));
607   PetscCall(VecCopy(v, locv));
608   PetscCall(DMGetLocalSection(dm, &section));
609   PetscCall(PetscSectionGetNumFields(section, &numFields));
610   if (!numFields) {
611     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
612     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
613   } else {
614     PetscInt f;
615 
616     for (f = 0; f < numFields; f++) {
617       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
618       if (ft == PETSC_VTK_INVALID) continue;
619       PetscCall(PetscObjectReference((PetscObject)locv));
620       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
621     }
622     PetscCall(VecDestroy(&locv));
623   }
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
628 {
629   DM        dm;
630   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
640   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
641   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
642     PetscInt    i, numFields;
643     PetscObject fe;
644     PetscBool   fem  = PETSC_FALSE;
645     Vec         locv = v;
646     const char *name;
647     PetscInt    step;
648     PetscReal   time;
649 
650     PetscCall(DMGetNumFields(dm, &numFields));
651     for (i = 0; i < numFields; i++) {
652       PetscCall(DMGetField(dm, i, NULL, &fe));
653       if (fe->classid == PETSCFE_CLASSID) {
654         fem = PETSC_TRUE;
655         break;
656       }
657     }
658     if (fem) {
659       PetscObject isZero;
660 
661       PetscCall(DMGetLocalVector(dm, &locv));
662       PetscCall(PetscObjectGetName((PetscObject)v, &name));
663       PetscCall(PetscObjectSetName((PetscObject)locv, name));
664       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
665       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
666       PetscCall(VecCopy(v, locv));
667       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
668       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
669     }
670     if (isvtk) {
671       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
672     } else if (ishdf5) {
673 #if defined(PETSC_HAVE_HDF5)
674       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
675 #else
676       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
677 #endif
678     } else if (isdraw) {
679       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
680     } else if (ispython) {
681       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
682     } else if (isglvis) {
683       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
684       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
685       PetscCall(VecView_GLVis(locv, viewer));
686     } else if (iscgns) {
687 #if defined(PETSC_HAVE_CGNS)
688       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
689 #else
690       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
691 #endif
692     }
693     if (fem) {
694       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
695       PetscCall(DMRestoreLocalVector(dm, &locv));
696     }
697   } else {
698     PetscBool isseq;
699 
700     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
701     if (isseq) PetscCall(VecView_Seq(v, viewer));
702     else PetscCall(VecView_MPI(v, viewer));
703   }
704   PetscFunctionReturn(PETSC_SUCCESS);
705 }
706 
707 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
708 {
709   DM        dm;
710   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
711 
712   PetscFunctionBegin;
713   PetscCall(VecGetDM(v, &dm));
714   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
715   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
716   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
721   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
722   if (isvtk || isdraw || isglvis || iscgns || ispython) {
723     Vec         locv;
724     PetscObject isZero;
725     const char *name;
726 
727     PetscCall(DMGetLocalVector(dm, &locv));
728     PetscCall(PetscObjectGetName((PetscObject)v, &name));
729     PetscCall(PetscObjectSetName((PetscObject)locv, name));
730     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
731     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
732     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
733     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
734     PetscCall(VecView_Plex_Local(locv, viewer));
735     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
736     PetscCall(DMRestoreLocalVector(dm, &locv));
737   } else if (ishdf5) {
738 #if defined(PETSC_HAVE_HDF5)
739     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
740 #else
741     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
742 #endif
743   } else if (isexodusii) {
744 #if defined(PETSC_HAVE_EXODUSII)
745     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
746 #else
747     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
748 #endif
749   } else {
750     PetscBool isseq;
751 
752     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
753     if (isseq) PetscCall(VecView_Seq(v, viewer));
754     else PetscCall(VecView_MPI(v, viewer));
755   }
756   PetscFunctionReturn(PETSC_SUCCESS);
757 }
758 
759 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
760 {
761   DM                dm;
762   MPI_Comm          comm;
763   PetscViewerFormat format;
764   Vec               v;
765   PetscBool         isvtk, ishdf5;
766 
767   PetscFunctionBegin;
768   PetscCall(VecGetDM(originalv, &dm));
769   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
770   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
771   PetscCall(PetscViewerGetFormat(viewer, &format));
772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
774   if (format == PETSC_VIEWER_NATIVE) {
775     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
776     /* this need a better fix */
777     if (dm->useNatural) {
778       const char *vecname;
779       PetscInt    n, nroots;
780 
781       PetscCheck(dm->sfNatural, comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
782       PetscCall(VecGetLocalSize(originalv, &n));
783       PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
784       PetscCheck(n == nroots, comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
785       PetscCall(DMPlexCreateNaturalVector(dm, &v));
786       PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
787       PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
788       PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
789       PetscCall(PetscObjectSetName((PetscObject)v, vecname));
790     } else v = originalv;
791   } else v = originalv;
792 
793   if (ishdf5) {
794 #if defined(PETSC_HAVE_HDF5)
795     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
796 #else
797     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
798 #endif
799   } else if (isvtk) {
800     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
801   } else {
802     PetscBool isseq;
803 
804     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
805     if (isseq) PetscCall(VecView_Seq(v, viewer));
806     else PetscCall(VecView_MPI(v, viewer));
807   }
808   if (v != originalv) PetscCall(VecDestroy(&v));
809   PetscFunctionReturn(PETSC_SUCCESS);
810 }
811 
812 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
813 {
814   DM        dm;
815   PetscBool ishdf5;
816 
817   PetscFunctionBegin;
818   PetscCall(VecGetDM(v, &dm));
819   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
820   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
821   if (ishdf5) {
822     DM          dmBC;
823     Vec         gv;
824     const char *name;
825 
826     PetscCall(DMGetOutputDM(dm, &dmBC));
827     PetscCall(DMGetGlobalVector(dmBC, &gv));
828     PetscCall(PetscObjectGetName((PetscObject)v, &name));
829     PetscCall(PetscObjectSetName((PetscObject)gv, name));
830     PetscCall(VecLoad_Default(gv, viewer));
831     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
832     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
833     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
834   } else PetscCall(VecLoad_Default(v, viewer));
835   PetscFunctionReturn(PETSC_SUCCESS);
836 }
837 
838 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
839 {
840   DM        dm;
841   PetscBool ishdf5, isexodusii, iscgns;
842 
843   PetscFunctionBegin;
844   PetscCall(VecGetDM(v, &dm));
845   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
846   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
848   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
849   if (ishdf5) {
850 #if defined(PETSC_HAVE_HDF5)
851     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
852 #else
853     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
854 #endif
855   } else if (isexodusii) {
856 #if defined(PETSC_HAVE_EXODUSII)
857     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
858 #else
859     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
860 #endif
861   } else if (iscgns) {
862 #if defined(PETSC_HAVE_CGNS)
863     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
864 #else
865     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
866 #endif
867   } else PetscCall(VecLoad_Default(v, viewer));
868   PetscFunctionReturn(PETSC_SUCCESS);
869 }
870 
871 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
872 {
873   DM                dm;
874   PetscViewerFormat format;
875   PetscBool         ishdf5;
876 
877   PetscFunctionBegin;
878   PetscCall(VecGetDM(originalv, &dm));
879   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
880   PetscCall(PetscViewerGetFormat(viewer, &format));
881   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
882   if (format == PETSC_VIEWER_NATIVE) {
883     if (dm->useNatural) {
884       if (dm->sfNatural) {
885         if (ishdf5) {
886 #if defined(PETSC_HAVE_HDF5)
887           Vec         v;
888           const char *vecname;
889 
890           PetscCall(DMPlexCreateNaturalVector(dm, &v));
891           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
892           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
893           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
894           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
895           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
896           PetscCall(VecDestroy(&v));
897 #else
898           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
899 #endif
900         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
901       }
902     } else PetscCall(VecLoad_Default(originalv, viewer));
903   }
904   PetscFunctionReturn(PETSC_SUCCESS);
905 }
906 
907 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
908 {
909   PetscSection       coordSection;
910   Vec                coordinates;
911   DMLabel            depthLabel, celltypeLabel;
912   const char        *name[4];
913   const PetscScalar *a;
914   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
915 
916   PetscFunctionBegin;
917   PetscCall(DMGetDimension(dm, &dim));
918   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
919   PetscCall(DMGetCoordinateSection(dm, &coordSection));
920   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
921   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
922   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
923   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
924   PetscCall(VecGetArrayRead(coordinates, &a));
925   name[0]       = "vertex";
926   name[1]       = "edge";
927   name[dim - 1] = "face";
928   name[dim]     = "cell";
929   for (c = cStart; c < cEnd; ++c) {
930     PetscInt *closure = NULL;
931     PetscInt  closureSize, cl, ct;
932 
933     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
934     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
935     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
936     PetscCall(PetscViewerASCIIPushTab(viewer));
937     for (cl = 0; cl < closureSize * 2; cl += 2) {
938       PetscInt point = closure[cl], depth, dof, off, d, p;
939 
940       if ((point < pStart) || (point >= pEnd)) continue;
941       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
942       if (!dof) continue;
943       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
944       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
945       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
946       for (p = 0; p < dof / dim; ++p) {
947         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
948         for (d = 0; d < dim; ++d) {
949           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
950           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
951         }
952         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
953       }
954       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
955     }
956     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
957     PetscCall(PetscViewerASCIIPopTab(viewer));
958   }
959   PetscCall(VecRestoreArrayRead(coordinates, &a));
960   PetscFunctionReturn(PETSC_SUCCESS);
961 }
962 
963 typedef enum {
964   CS_CARTESIAN,
965   CS_POLAR,
966   CS_CYLINDRICAL,
967   CS_SPHERICAL
968 } CoordSystem;
969 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
970 
971 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
972 {
973   PetscInt i;
974 
975   PetscFunctionBegin;
976   if (dim > 3) {
977     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
978   } else {
979     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
980 
981     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
982     switch (cs) {
983     case CS_CARTESIAN:
984       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
985       break;
986     case CS_POLAR:
987       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
988       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
989       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
990       break;
991     case CS_CYLINDRICAL:
992       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
993       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
994       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
995       trcoords[2] = coords[2];
996       break;
997     case CS_SPHERICAL:
998       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
999       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1000       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1001       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1002       break;
1003     }
1004     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1005   }
1006   PetscFunctionReturn(PETSC_SUCCESS);
1007 }
1008 
1009 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1010 {
1011   DM_Plex          *mesh = (DM_Plex *)dm->data;
1012   DM                cdm, cdmCell;
1013   PetscSection      coordSection, coordSectionCell;
1014   Vec               coordinates, coordinatesCell;
1015   PetscViewerFormat format;
1016 
1017   PetscFunctionBegin;
1018   PetscCall(PetscViewerGetFormat(viewer, &format));
1019   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1020     const char *name;
1021     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1022     PetscInt    pStart, pEnd, p, numLabels, l;
1023     PetscMPIInt rank, size;
1024 
1025     PetscCall(DMGetCoordinateDM(dm, &cdm));
1026     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1027     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1028     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1029     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1030     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1031     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1032     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1033     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1034     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1035     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1036     PetscCall(DMGetDimension(dm, &dim));
1037     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1038     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1039     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1040     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1041     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1042     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1043     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1044     for (p = pStart; p < pEnd; ++p) {
1045       PetscInt dof, off, s;
1046 
1047       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1048       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1049       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1050     }
1051     PetscCall(PetscViewerFlush(viewer));
1052     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1053     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1054     for (p = pStart; p < pEnd; ++p) {
1055       PetscInt dof, off, c;
1056 
1057       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1058       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1059       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]));
1060     }
1061     PetscCall(PetscViewerFlush(viewer));
1062     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1063     if (coordSection && coordinates) {
1064       CoordSystem        cs = CS_CARTESIAN;
1065       const PetscScalar *array, *arrayCell = NULL;
1066       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1067       PetscMPIInt        rank;
1068       const char        *name;
1069 
1070       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1071       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1072       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1073       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1074       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1075       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1076       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1077       pStart = PetscMin(pvStart, pcStart);
1078       pEnd   = PetscMax(pvEnd, pcEnd);
1079       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1080       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1081       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1082       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1083 
1084       PetscCall(VecGetArrayRead(coordinates, &array));
1085       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1086       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1087       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1088       for (p = pStart; p < pEnd; ++p) {
1089         PetscInt dof, off;
1090 
1091         if (p >= pvStart && p < pvEnd) {
1092           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1093           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1094           if (dof) {
1095             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1096             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1097             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1098           }
1099         }
1100         if (cdmCell && p >= pcStart && p < pcEnd) {
1101           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1102           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1103           if (dof) {
1104             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1105             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1106             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1107           }
1108         }
1109       }
1110       PetscCall(PetscViewerFlush(viewer));
1111       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1112       PetscCall(VecRestoreArrayRead(coordinates, &array));
1113       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1114     }
1115     PetscCall(DMGetNumLabels(dm, &numLabels));
1116     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1117     for (l = 0; l < numLabels; ++l) {
1118       DMLabel     label;
1119       PetscBool   isdepth;
1120       const char *name;
1121 
1122       PetscCall(DMGetLabelName(dm, l, &name));
1123       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1124       if (isdepth) continue;
1125       PetscCall(DMGetLabel(dm, name, &label));
1126       PetscCall(DMLabelView(label, viewer));
1127     }
1128     if (size > 1) {
1129       PetscSF sf;
1130 
1131       PetscCall(DMGetPointSF(dm, &sf));
1132       PetscCall(PetscSFView(sf, viewer));
1133     }
1134     if (mesh->periodic.face_sfs)
1135       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1136     PetscCall(PetscViewerFlush(viewer));
1137   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1138     const char  *name, *color;
1139     const char  *defcolors[3]  = {"gray", "orange", "green"};
1140     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1141     char         lname[PETSC_MAX_PATH_LEN];
1142     PetscReal    scale      = 2.0;
1143     PetscReal    tikzscale  = 1.0;
1144     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1145     double       tcoords[3];
1146     PetscScalar *coords;
1147     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;
1148     PetscMPIInt  rank, size;
1149     char       **names, **colors, **lcolors;
1150     PetscBool    flg, lflg;
1151     PetscBT      wp = NULL;
1152     PetscInt     pEnd, pStart;
1153 
1154     PetscCall(DMGetCoordinateDM(dm, &cdm));
1155     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1156     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1157     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1158     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1159     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1160     PetscCall(DMGetDimension(dm, &dim));
1161     PetscCall(DMPlexGetDepth(dm, &depth));
1162     PetscCall(DMGetNumLabels(dm, &numLabels));
1163     numLabels  = PetscMax(numLabels, 10);
1164     numColors  = 10;
1165     numLColors = 10;
1166     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1167     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1168     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1169     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1170     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1171     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1172     n = 4;
1173     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1174     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1175     n = 4;
1176     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1177     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1178     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1179     if (!useLabels) numLabels = 0;
1180     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1181     if (!useColors) {
1182       numColors = 3;
1183       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1184     }
1185     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1186     if (!useColors) {
1187       numLColors = 4;
1188       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1189     }
1190     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1191     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1192     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1193     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1194     if (depth < dim) plotEdges = PETSC_FALSE;
1195     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1196 
1197     /* filter points with labelvalue != labeldefaultvalue */
1198     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1199     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1200     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1201     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1202     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1203     if (lflg) {
1204       DMLabel lbl;
1205 
1206       PetscCall(DMGetLabel(dm, lname, &lbl));
1207       if (lbl) {
1208         PetscInt val, defval;
1209 
1210         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1211         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1212         for (c = pStart; c < pEnd; c++) {
1213           PetscInt *closure = NULL;
1214           PetscInt  closureSize;
1215 
1216           PetscCall(DMLabelGetValue(lbl, c, &val));
1217           if (val == defval) continue;
1218 
1219           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1220           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225 
1226     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1227     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1228     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1229     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1230 \\documentclass[tikz]{standalone}\n\n\
1231 \\usepackage{pgflibraryshapes}\n\
1232 \\usetikzlibrary{backgrounds}\n\
1233 \\usetikzlibrary{arrows}\n\
1234 \\begin{document}\n"));
1235     if (size > 1) {
1236       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1237       for (p = 0; p < size; ++p) {
1238         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1239         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1240       }
1241       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1242     }
1243     if (drawHasse) {
1244       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1245 
1246       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1247       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1248       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1249       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1262     }
1263     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1264 
1265     /* Plot vertices */
1266     PetscCall(VecGetArray(coordinates, &coords));
1267     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1268     for (v = vStart; v < vEnd; ++v) {
1269       PetscInt  off, dof, d;
1270       PetscBool isLabeled = PETSC_FALSE;
1271 
1272       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1273       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1274       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1275       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1276       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1277       for (d = 0; d < dof; ++d) {
1278         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1279         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1280       }
1281       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1282       if (dim == 3) {
1283         PetscReal tmp = tcoords[1];
1284         tcoords[1]    = tcoords[2];
1285         tcoords[2]    = -tmp;
1286       }
1287       for (d = 0; d < dof; ++d) {
1288         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1289         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1290       }
1291       if (drawHasse) color = colors[0 % numColors];
1292       else color = colors[rank % numColors];
1293       for (l = 0; l < numLabels; ++l) {
1294         PetscInt val;
1295         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1296         if (val >= 0) {
1297           color     = lcolors[l % numLColors];
1298           isLabeled = PETSC_TRUE;
1299           break;
1300         }
1301       }
1302       if (drawNumbers[0]) {
1303         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1304       } else if (drawColors[0]) {
1305         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1306       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1307     }
1308     PetscCall(VecRestoreArray(coordinates, &coords));
1309     PetscCall(PetscViewerFlush(viewer));
1310     /* Plot edges */
1311     if (plotEdges) {
1312       PetscCall(VecGetArray(coordinates, &coords));
1313       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1314       for (e = eStart; e < eEnd; ++e) {
1315         const PetscInt *cone;
1316         PetscInt        coneSize, offA, offB, dof, d;
1317 
1318         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1319         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1320         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1321         PetscCall(DMPlexGetCone(dm, e, &cone));
1322         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1323         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1324         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1325         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1326         for (d = 0; d < dof; ++d) {
1327           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1328           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1329         }
1330         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1331         if (dim == 3) {
1332           PetscReal tmp = tcoords[1];
1333           tcoords[1]    = tcoords[2];
1334           tcoords[2]    = -tmp;
1335         }
1336         for (d = 0; d < dof; ++d) {
1337           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1338           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1339         }
1340         if (drawHasse) color = colors[1 % numColors];
1341         else color = colors[rank % numColors];
1342         for (l = 0; l < numLabels; ++l) {
1343           PetscInt val;
1344           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1345           if (val >= 0) {
1346             color = lcolors[l % numLColors];
1347             break;
1348           }
1349         }
1350         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1351       }
1352       PetscCall(VecRestoreArray(coordinates, &coords));
1353       PetscCall(PetscViewerFlush(viewer));
1354       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1355     }
1356     /* Plot cells */
1357     if (dim == 3 || !drawNumbers[1]) {
1358       for (e = eStart; e < eEnd; ++e) {
1359         const PetscInt *cone;
1360 
1361         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1362         color = colors[rank % numColors];
1363         for (l = 0; l < numLabels; ++l) {
1364           PetscInt val;
1365           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1366           if (val >= 0) {
1367             color = lcolors[l % numLColors];
1368             break;
1369           }
1370         }
1371         PetscCall(DMPlexGetCone(dm, e, &cone));
1372         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1373       }
1374     } else {
1375       DMPolytopeType ct;
1376 
1377       /* Drawing a 2D polygon */
1378       for (c = cStart; c < cEnd; ++c) {
1379         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1380         PetscCall(DMPlexGetCellType(dm, c, &ct));
1381         if (DMPolytopeTypeIsHybrid(ct)) {
1382           const PetscInt *cone;
1383           PetscInt        coneSize, e;
1384 
1385           PetscCall(DMPlexGetCone(dm, c, &cone));
1386           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1387           for (e = 0; e < coneSize; ++e) {
1388             const PetscInt *econe;
1389 
1390             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1391             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));
1392           }
1393         } else {
1394           PetscInt *closure = NULL;
1395           PetscInt  closureSize, Nv = 0, v;
1396 
1397           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1398           for (p = 0; p < closureSize * 2; p += 2) {
1399             const PetscInt point = closure[p];
1400 
1401             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1402           }
1403           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1404           for (v = 0; v <= Nv; ++v) {
1405             const PetscInt vertex = closure[v % Nv];
1406 
1407             if (v > 0) {
1408               if (plotEdges) {
1409                 const PetscInt *edge;
1410                 PetscInt        endpoints[2], ne;
1411 
1412                 endpoints[0] = closure[v - 1];
1413                 endpoints[1] = vertex;
1414                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1415                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1416                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1417                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1418               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1419             }
1420             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1421           }
1422           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1423           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1424         }
1425       }
1426     }
1427     for (c = cStart; c < cEnd; ++c) {
1428       double             ccoords[3] = {0.0, 0.0, 0.0};
1429       PetscBool          isLabeled  = PETSC_FALSE;
1430       PetscScalar       *cellCoords = NULL;
1431       const PetscScalar *array;
1432       PetscInt           numCoords, cdim, d;
1433       PetscBool          isDG;
1434 
1435       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1436       PetscCall(DMGetCoordinateDim(dm, &cdim));
1437       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1438       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1439       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1440       for (p = 0; p < numCoords / cdim; ++p) {
1441         for (d = 0; d < cdim; ++d) {
1442           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1443           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1444         }
1445         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1446         if (cdim == 3) {
1447           PetscReal tmp = tcoords[1];
1448           tcoords[1]    = tcoords[2];
1449           tcoords[2]    = -tmp;
1450         }
1451         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1452       }
1453       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1454       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1455       for (d = 0; d < cdim; ++d) {
1456         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1457         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1458       }
1459       if (drawHasse) color = colors[depth % numColors];
1460       else color = colors[rank % numColors];
1461       for (l = 0; l < numLabels; ++l) {
1462         PetscInt val;
1463         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1464         if (val >= 0) {
1465           color     = lcolors[l % numLColors];
1466           isLabeled = PETSC_TRUE;
1467           break;
1468         }
1469       }
1470       if (drawNumbers[dim]) {
1471         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1472       } else if (drawColors[dim]) {
1473         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1474       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1475     }
1476     if (drawHasse) {
1477       int height = 0;
1478 
1479       color = colors[depth % numColors];
1480       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1481       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1482       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1483       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1485 
1486       if (depth > 2) {
1487         color = colors[1 % numColors];
1488         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1489         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1490         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1491         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1493       }
1494 
1495       color = colors[1 % numColors];
1496       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1497       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1498       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1499       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1501 
1502       color = colors[0 % numColors];
1503       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1504       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1505       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1506       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1508 
1509       for (p = pStart; p < pEnd; ++p) {
1510         const PetscInt *cone;
1511         PetscInt        coneSize, cp;
1512 
1513         PetscCall(DMPlexGetCone(dm, p, &cone));
1514         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1515         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1516       }
1517     }
1518     PetscCall(PetscViewerFlush(viewer));
1519     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1520     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1521     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1522     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1523     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1524     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1525     PetscCall(PetscFree3(names, colors, lcolors));
1526     PetscCall(PetscBTDestroy(&wp));
1527   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1528     Vec                    cown, acown;
1529     VecScatter             sct;
1530     ISLocalToGlobalMapping g2l;
1531     IS                     gid, acis;
1532     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1533     MPI_Group              ggroup, ngroup;
1534     PetscScalar           *array, nid;
1535     const PetscInt        *idxs;
1536     PetscInt              *idxs2, *start, *adjacency, *work;
1537     PetscInt64             lm[3], gm[3];
1538     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1539     PetscMPIInt            d1, d2, rank;
1540 
1541     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1542     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1543 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1544     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1545 #endif
1546     if (ncomm != MPI_COMM_NULL) {
1547       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1548       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1549       d1 = 0;
1550       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1551       nid = d2;
1552       PetscCallMPI(MPI_Group_free(&ggroup));
1553       PetscCallMPI(MPI_Group_free(&ngroup));
1554       PetscCallMPI(MPI_Comm_free(&ncomm));
1555     } else nid = 0.0;
1556 
1557     /* Get connectivity */
1558     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1559     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1560 
1561     /* filter overlapped local cells */
1562     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1563     PetscCall(ISGetIndices(gid, &idxs));
1564     PetscCall(ISGetLocalSize(gid, &cum));
1565     PetscCall(PetscMalloc1(cum, &idxs2));
1566     for (c = cStart, cum = 0; c < cEnd; c++) {
1567       if (idxs[c - cStart] < 0) continue;
1568       idxs2[cum++] = idxs[c - cStart];
1569     }
1570     PetscCall(ISRestoreIndices(gid, &idxs));
1571     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1572     PetscCall(ISDestroy(&gid));
1573     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1574 
1575     /* support for node-aware cell locality */
1576     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1577     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1578     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1579     PetscCall(VecGetArray(cown, &array));
1580     for (c = 0; c < numVertices; c++) array[c] = nid;
1581     PetscCall(VecRestoreArray(cown, &array));
1582     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1583     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1584     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1585     PetscCall(ISDestroy(&acis));
1586     PetscCall(VecScatterDestroy(&sct));
1587     PetscCall(VecDestroy(&cown));
1588 
1589     /* compute edgeCut */
1590     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1591     PetscCall(PetscMalloc1(cum, &work));
1592     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1593     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1594     PetscCall(ISDestroy(&gid));
1595     PetscCall(VecGetArray(acown, &array));
1596     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1597       PetscInt totl;
1598 
1599       totl = start[c + 1] - start[c];
1600       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1601       for (i = 0; i < totl; i++) {
1602         if (work[i] < 0) {
1603           ect += 1;
1604           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1605         }
1606       }
1607     }
1608     PetscCall(PetscFree(work));
1609     PetscCall(VecRestoreArray(acown, &array));
1610     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1611     lm[1] = -numVertices;
1612     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1613     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1614     lm[0] = ect;                     /* edgeCut */
1615     lm[1] = ectn;                    /* node-aware edgeCut */
1616     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1617     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1618     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1619 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1620     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1621 #else
1622     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1623 #endif
1624     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1625     PetscCall(PetscFree(start));
1626     PetscCall(PetscFree(adjacency));
1627     PetscCall(VecDestroy(&acown));
1628   } else {
1629     const char    *name;
1630     PetscInt      *sizes, *hybsizes, *ghostsizes;
1631     PetscInt       locDepth, depth, cellHeight, dim, d;
1632     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1633     PetscInt       numLabels, l, maxSize = 17;
1634     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1635     MPI_Comm       comm;
1636     PetscMPIInt    size, rank;
1637 
1638     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1639     PetscCallMPI(MPI_Comm_size(comm, &size));
1640     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1641     PetscCall(DMGetDimension(dm, &dim));
1642     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1643     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1644     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1645     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1646     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1647     PetscCall(DMPlexGetDepth(dm, &locDepth));
1648     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1649     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1650     gcNum = gcEnd - gcStart;
1651     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1652     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1653     for (d = 0; d <= depth; d++) {
1654       PetscInt Nc[2] = {0, 0}, ict;
1655 
1656       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1657       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1658       ict = ct0;
1659       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1660       ct0 = (DMPolytopeType)ict;
1661       for (p = pStart; p < pEnd; ++p) {
1662         DMPolytopeType ct;
1663 
1664         PetscCall(DMPlexGetCellType(dm, p, &ct));
1665         if (ct == ct0) ++Nc[0];
1666         else ++Nc[1];
1667       }
1668       if (size < maxSize) {
1669         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1670         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1671         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1672         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1673         for (p = 0; p < size; ++p) {
1674           if (rank == 0) {
1675             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1676             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1677             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1678           }
1679         }
1680       } else {
1681         PetscInt locMinMax[2];
1682 
1683         locMinMax[0] = Nc[0] + Nc[1];
1684         locMinMax[1] = Nc[0] + Nc[1];
1685         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1686         locMinMax[0] = Nc[1];
1687         locMinMax[1] = Nc[1];
1688         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1689         if (d == depth) {
1690           locMinMax[0] = gcNum;
1691           locMinMax[1] = gcNum;
1692           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1693         }
1694         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1695         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1696         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1697         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1698       }
1699       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1700     }
1701     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1702     {
1703       const PetscReal *maxCell;
1704       const PetscReal *L;
1705       PetscBool        localized;
1706 
1707       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1708       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1709       if (L || localized) {
1710         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1711         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1712         if (L) {
1713           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1714           for (d = 0; d < dim; ++d) {
1715             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1716             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1717           }
1718           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1719         }
1720         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1721         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1722       }
1723     }
1724     PetscCall(DMGetNumLabels(dm, &numLabels));
1725     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1726     for (l = 0; l < numLabels; ++l) {
1727       DMLabel     label;
1728       const char *name;
1729       PetscInt   *values;
1730       PetscInt    numValues, v;
1731 
1732       PetscCall(DMGetLabelName(dm, l, &name));
1733       PetscCall(DMGetLabel(dm, name, &label));
1734       PetscCall(DMLabelGetNumValues(label, &numValues));
1735       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1736 
1737       { // Extract array of DMLabel values so it can be sorted
1738         IS              is_values;
1739         const PetscInt *is_values_local = NULL;
1740 
1741         PetscCall(DMLabelGetValueIS(label, &is_values));
1742         PetscCall(ISGetIndices(is_values, &is_values_local));
1743         PetscCall(PetscMalloc1(numValues, &values));
1744         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1745         PetscCall(PetscSortInt(numValues, values));
1746         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1747         PetscCall(ISDestroy(&is_values));
1748       }
1749       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1750       for (v = 0; v < numValues; ++v) {
1751         PetscInt size;
1752 
1753         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1754         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1755         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1756       }
1757       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1758       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1759       PetscCall(PetscFree(values));
1760     }
1761     {
1762       char    **labelNames;
1763       PetscInt  Nl = numLabels;
1764       PetscBool flg;
1765 
1766       PetscCall(PetscMalloc1(Nl, &labelNames));
1767       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1768       for (l = 0; l < Nl; ++l) {
1769         DMLabel label;
1770 
1771         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1772         if (flg) {
1773           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1774           PetscCall(DMLabelView(label, viewer));
1775         }
1776         PetscCall(PetscFree(labelNames[l]));
1777       }
1778       PetscCall(PetscFree(labelNames));
1779     }
1780     /* If no fields are specified, people do not want to see adjacency */
1781     if (dm->Nf) {
1782       PetscInt f;
1783 
1784       for (f = 0; f < dm->Nf; ++f) {
1785         const char *name;
1786 
1787         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1788         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1789         PetscCall(PetscViewerASCIIPushTab(viewer));
1790         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1791         if (dm->fields[f].adjacency[0]) {
1792           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1793           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1794         } else {
1795           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1796           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1797         }
1798         PetscCall(PetscViewerASCIIPopTab(viewer));
1799       }
1800     }
1801     DMPlexTransform tr;
1802 
1803     PetscCall(DMPlexGetTransform(dm, &tr));
1804     if (tr) {
1805       PetscCall(PetscViewerASCIIPushTab(viewer));
1806       PetscCall(PetscViewerASCIIPrintf(viewer, "Created using transform:\n"));
1807       PetscCall(DMPlexTransformView(tr, viewer));
1808       PetscCall(PetscViewerASCIIPopTab(viewer));
1809     }
1810     PetscCall(DMGetCoarseDM(dm, &cdm));
1811     if (cdm) {
1812       PetscCall(PetscViewerASCIIPushTab(viewer));
1813       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1814       PetscCall(DMPlexView_Ascii(cdm, viewer));
1815       PetscCall(PetscViewerASCIIPopTab(viewer));
1816     }
1817   }
1818   PetscFunctionReturn(PETSC_SUCCESS);
1819 }
1820 
1821 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt lC, PetscInt cC, PetscInt cell, const PetscScalar coords[])
1822 {
1823   DMPolytopeType ct;
1824   PetscMPIInt    rank;
1825   PetscInt       cdim;
1826   int            lineColor, cellColor;
1827 
1828   PetscFunctionBegin;
1829   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1830   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1831   PetscCall(DMGetCoordinateDim(dm, &cdim));
1832   lineColor = (int)(lC < 0 ? PETSC_DRAW_BLACK : lC);
1833   cellColor = (int)(cC < 0 ? PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2 : cC);
1834   switch (ct) {
1835   case DM_POLYTOPE_SEGMENT:
1836   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1837     switch (cdim) {
1838     case 1: {
1839       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1840       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1841 
1842       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, lineColor));
1843       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, lineColor));
1844       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, lineColor));
1845     } break;
1846     case 2: {
1847       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1848       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1849       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1850 
1851       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1852       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, lineColor));
1853       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, lineColor));
1854     } break;
1855     default:
1856       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1857     }
1858     break;
1859   case DM_POLYTOPE_TRIANGLE:
1860     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1861     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1862     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1863     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1864     break;
1865   case DM_POLYTOPE_QUADRILATERAL:
1866     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1867     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), cellColor, cellColor, cellColor));
1868     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1869     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1870     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1871     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1872     break;
1873   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1874     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1875     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1876     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1877     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1878     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1879     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1880     break;
1881   case DM_POLYTOPE_FV_GHOST:
1882     break;
1883   default:
1884     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1885   }
1886   PetscFunctionReturn(PETSC_SUCCESS);
1887 }
1888 
1889 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1890 {
1891   PetscReal   centroid[2] = {0., 0.};
1892   PetscMPIInt rank;
1893   PetscMPIInt fillColor;
1894 
1895   PetscFunctionBegin;
1896   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1897   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1898   for (PetscInt v = 0; v < Nv; ++v) {
1899     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1900     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1901   }
1902   for (PetscInt e = 0; e < Nv; ++e) {
1903     refCoords[0] = refVertices[e * 2 + 0];
1904     refCoords[1] = refVertices[e * 2 + 1];
1905     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1906       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1907       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1908     }
1909     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1910     for (PetscInt d = 0; d < edgeDiv; ++d) {
1911       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));
1912       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1913     }
1914   }
1915   PetscFunctionReturn(PETSC_SUCCESS);
1916 }
1917 
1918 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1919 {
1920   DMPolytopeType ct;
1921 
1922   PetscFunctionBegin;
1923   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1924   switch (ct) {
1925   case DM_POLYTOPE_TRIANGLE: {
1926     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1927 
1928     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1929   } break;
1930   case DM_POLYTOPE_QUADRILATERAL: {
1931     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1932 
1933     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1934   } break;
1935   default:
1936     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1937   }
1938   PetscFunctionReturn(PETSC_SUCCESS);
1939 }
1940 
1941 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1942 {
1943   PetscDraw    draw;
1944   DM           cdm;
1945   PetscSection coordSection;
1946   Vec          coordinates;
1947   PetscReal    xyl[3], xyr[3];
1948   PetscReal   *refCoords, *edgeCoords;
1949   PetscBool    isnull, drawAffine;
1950   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv, lineColor = PETSC_DETERMINE, cellColor = PETSC_DETERMINE;
1951 
1952   PetscFunctionBegin;
1953   PetscCall(DMGetCoordinateDim(dm, &dim));
1954   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1955   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1956   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1957   edgeDiv    = cDegree + 1;
1958   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_line_color", &lineColor, NULL));
1959   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_cell_color", &cellColor, NULL));
1960   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1961   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1962   PetscCall(DMGetCoordinateDM(dm, &cdm));
1963   PetscCall(DMGetLocalSection(cdm, &coordSection));
1964   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1965   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1966   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1967 
1968   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1969   PetscCall(PetscDrawIsNull(draw, &isnull));
1970   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1971   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1972 
1973   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1974   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1975   PetscCall(PetscDrawClear(draw));
1976 
1977   for (c = cStart; c < cEnd; ++c) {
1978     PetscScalar       *coords = NULL;
1979     const PetscScalar *coords_arr;
1980     PetscInt           numCoords;
1981     PetscBool          isDG;
1982 
1983     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1984     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, lineColor, cellColor, c, coords));
1985     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1986     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1987   }
1988   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1989   PetscCall(PetscDrawFlush(draw));
1990   PetscCall(PetscDrawPause(draw));
1991   PetscCall(PetscDrawSave(draw));
1992   PetscFunctionReturn(PETSC_SUCCESS);
1993 }
1994 
1995 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1996 {
1997   DM           odm = dm, rdm = dm, cdm;
1998   PetscFE      fe;
1999   PetscSpace   sp;
2000   PetscClassId id;
2001   PetscInt     degree;
2002   PetscBool    hoView = PETSC_TRUE;
2003 
2004   PetscFunctionBegin;
2005   PetscObjectOptionsBegin((PetscObject)dm);
2006   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2007   PetscOptionsEnd();
2008   PetscCall(PetscObjectReference((PetscObject)dm));
2009   *hdm = dm;
2010   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2011   PetscCall(DMGetCoordinateDM(dm, &cdm));
2012   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2013   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2014   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2015   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2016   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2017   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2018     DM  cdm, rcdm;
2019     Mat In;
2020     Vec cl, rcl;
2021 
2022     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2023     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, PETSC_FALSE));
2024     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2025     PetscCall(DMGetCoordinateDM(odm, &cdm));
2026     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2027     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2028     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2029     PetscCall(DMSetCoarseDM(rcdm, cdm));
2030     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2031     PetscCall(MatMult(In, cl, rcl));
2032     PetscCall(MatDestroy(&In));
2033     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2034     PetscCall(DMDestroy(&odm));
2035     odm = rdm;
2036   }
2037   *hdm = rdm;
2038   PetscFunctionReturn(PETSC_SUCCESS);
2039 }
2040 
2041 #if defined(PETSC_HAVE_EXODUSII)
2042   #include <exodusII.h>
2043   #include <petscviewerexodusii.h>
2044 #endif
2045 
2046 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2047 {
2048   PetscBool isascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2049   char      name[PETSC_MAX_PATH_LEN];
2050 
2051   PetscFunctionBegin;
2052   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2053   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2054   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
2055   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2056   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2057   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2060   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2061   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2062   if (isascii) {
2063     PetscViewerFormat format;
2064     PetscCall(PetscViewerGetFormat(viewer, &format));
2065     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2066     else PetscCall(DMPlexView_Ascii(dm, viewer));
2067   } else if (ishdf5) {
2068 #if defined(PETSC_HAVE_HDF5)
2069     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2070 #else
2071     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2072 #endif
2073   } else if (isvtk) {
2074     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2075   } else if (isdraw) {
2076     DM hdm;
2077 
2078     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2079     PetscCall(DMPlexView_Draw(hdm, viewer));
2080     PetscCall(DMDestroy(&hdm));
2081   } else if (isglvis) {
2082     PetscCall(DMPlexView_GLVis(dm, viewer));
2083 #if defined(PETSC_HAVE_EXODUSII)
2084   } else if (isexodus) {
2085     /*
2086       ExodusII requires that all sets be part of exactly one cell set.
2087       If the dm does not have a "Cell Sets" label defined, we create one
2088       with ID 1, containing all cells.
2089       Note that if the Cell Sets label is defined but does not cover all cells,
2090       we may still have a problem. This should probably be checked here or in the viewer;
2091     */
2092     PetscInt numCS;
2093     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2094     if (!numCS) {
2095       PetscInt cStart, cEnd, c;
2096       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2097       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2098       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2099     }
2100     PetscCall(DMView_PlexExodusII(dm, viewer));
2101 #endif
2102 #if defined(PETSC_HAVE_CGNS)
2103   } else if (iscgns) {
2104     PetscCall(DMView_PlexCGNS(dm, viewer));
2105 #endif
2106   } else if (ispython) {
2107     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2108   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2109   /* Optionally view the partition */
2110   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2111   if (flg) {
2112     Vec ranks;
2113     PetscCall(DMPlexCreateRankField(dm, &ranks));
2114     PetscCall(VecView(ranks, viewer));
2115     PetscCall(VecDestroy(&ranks));
2116   }
2117   /* Optionally view a label */
2118   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2119   if (flg) {
2120     DMLabel label;
2121     Vec     val;
2122 
2123     PetscCall(DMGetLabel(dm, name, &label));
2124     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2125     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2126     PetscCall(VecView(val, viewer));
2127     PetscCall(VecDestroy(&val));
2128   }
2129   PetscFunctionReturn(PETSC_SUCCESS);
2130 }
2131 
2132 /*@
2133   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2134 
2135   Collective
2136 
2137   Input Parameters:
2138 + dm     - The `DM` whose topology is to be saved
2139 - viewer - The `PetscViewer` to save it in
2140 
2141   Level: advanced
2142 
2143 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2144 @*/
2145 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2146 {
2147   PetscBool ishdf5;
2148 
2149   PetscFunctionBegin;
2150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2151   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2152   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2153   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2154   if (ishdf5) {
2155 #if defined(PETSC_HAVE_HDF5)
2156     IS                globalPointNumbering;
2157     PetscViewerFormat format;
2158 
2159     PetscCall(PetscViewerGetFormat(viewer, &format));
2160     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2161     PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2162     PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2163     PetscCall(ISDestroy(&globalPointNumbering));
2164 #else
2165     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2166 #endif
2167   }
2168   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2169   PetscFunctionReturn(PETSC_SUCCESS);
2170 }
2171 
2172 /*@
2173   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2174 
2175   Collective
2176 
2177   Input Parameters:
2178 + dm     - The `DM` whose coordinates are to be saved
2179 - viewer - The `PetscViewer` for saving
2180 
2181   Level: advanced
2182 
2183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2184 @*/
2185 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2186 {
2187   PetscBool ishdf5;
2188 
2189   PetscFunctionBegin;
2190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2191   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2192   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2193   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2194   if (ishdf5) {
2195 #if defined(PETSC_HAVE_HDF5)
2196     PetscViewerFormat format;
2197 
2198     PetscCall(PetscViewerGetFormat(viewer, &format));
2199     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2200     PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2201 #else
2202     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2203 #endif
2204   }
2205   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2206   PetscFunctionReturn(PETSC_SUCCESS);
2207 }
2208 
2209 /*@
2210   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2211 
2212   Collective
2213 
2214   Input Parameters:
2215 + dm     - The `DM` whose labels are to be saved
2216 - viewer - The `PetscViewer` for saving
2217 
2218   Level: advanced
2219 
2220 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2221 @*/
2222 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2223 {
2224   PetscBool ishdf5;
2225 
2226   PetscFunctionBegin;
2227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2228   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2229   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2230   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2231   if (ishdf5) {
2232 #if defined(PETSC_HAVE_HDF5)
2233     IS                globalPointNumbering;
2234     PetscViewerFormat format;
2235 
2236     PetscCall(PetscViewerGetFormat(viewer, &format));
2237     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2238     PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2239     PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2240     PetscCall(ISDestroy(&globalPointNumbering));
2241 #else
2242     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2243 #endif
2244   }
2245   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2246   PetscFunctionReturn(PETSC_SUCCESS);
2247 }
2248 
2249 /*@
2250   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2251 
2252   Collective
2253 
2254   Input Parameters:
2255 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2256 . viewer    - The `PetscViewer` for saving
2257 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2258 
2259   Level: advanced
2260 
2261   Notes:
2262   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.
2263 
2264   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.
2265 
2266 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2267 @*/
2268 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2269 {
2270   PetscBool ishdf5;
2271 
2272   PetscFunctionBegin;
2273   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2274   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2275   if (!sectiondm) sectiondm = dm;
2276   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2277   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2278   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2279   if (ishdf5) {
2280 #if defined(PETSC_HAVE_HDF5)
2281     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2282 #else
2283     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2284 #endif
2285   }
2286   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2287   PetscFunctionReturn(PETSC_SUCCESS);
2288 }
2289 
2290 /*@
2291   DMPlexGlobalVectorView - Saves a global vector
2292 
2293   Collective
2294 
2295   Input Parameters:
2296 + dm        - The `DM` that represents the topology
2297 . viewer    - The `PetscViewer` to save data with
2298 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2299 - vec       - The global vector to be saved
2300 
2301   Level: advanced
2302 
2303   Notes:
2304   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.
2305 
2306   Calling sequence:
2307 .vb
2308        DMCreate(PETSC_COMM_WORLD, &dm);
2309        DMSetType(dm, DMPLEX);
2310        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2311        DMClone(dm, &sectiondm);
2312        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2313        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2314        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2315        PetscSectionSetChart(section, pStart, pEnd);
2316        PetscSectionSetUp(section);
2317        DMSetLocalSection(sectiondm, section);
2318        PetscSectionDestroy(&section);
2319        DMGetGlobalVector(sectiondm, &vec);
2320        PetscObjectSetName((PetscObject)vec, "vec_name");
2321        DMPlexTopologyView(dm, viewer);
2322        DMPlexSectionView(dm, viewer, sectiondm);
2323        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2324        DMRestoreGlobalVector(sectiondm, &vec);
2325        DMDestroy(&sectiondm);
2326        DMDestroy(&dm);
2327 .ve
2328 
2329 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2330 @*/
2331 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2332 {
2333   PetscBool ishdf5;
2334 
2335   PetscFunctionBegin;
2336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2337   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2338   if (!sectiondm) sectiondm = dm;
2339   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2340   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2341   /* Check consistency */
2342   {
2343     PetscSection section;
2344     PetscBool    includesConstraints;
2345     PetscInt     m, m1;
2346 
2347     PetscCall(VecGetLocalSize(vec, &m1));
2348     PetscCall(DMGetGlobalSection(sectiondm, &section));
2349     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2350     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2351     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2352     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2353   }
2354   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2355   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2356   if (ishdf5) {
2357 #if defined(PETSC_HAVE_HDF5)
2358     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2359 #else
2360     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2361 #endif
2362   }
2363   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2364   PetscFunctionReturn(PETSC_SUCCESS);
2365 }
2366 
2367 /*@
2368   DMPlexLocalVectorView - Saves a local vector
2369 
2370   Collective
2371 
2372   Input Parameters:
2373 + dm        - The `DM` that represents the topology
2374 . viewer    - The `PetscViewer` to save data with
2375 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2376 - vec       - The local vector to be saved
2377 
2378   Level: advanced
2379 
2380   Note:
2381   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.
2382 
2383   Calling sequence:
2384 .vb
2385        DMCreate(PETSC_COMM_WORLD, &dm);
2386        DMSetType(dm, DMPLEX);
2387        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2388        DMClone(dm, &sectiondm);
2389        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2390        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2391        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2392        PetscSectionSetChart(section, pStart, pEnd);
2393        PetscSectionSetUp(section);
2394        DMSetLocalSection(sectiondm, section);
2395        DMGetLocalVector(sectiondm, &vec);
2396        PetscObjectSetName((PetscObject)vec, "vec_name");
2397        DMPlexTopologyView(dm, viewer);
2398        DMPlexSectionView(dm, viewer, sectiondm);
2399        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2400        DMRestoreLocalVector(sectiondm, &vec);
2401        DMDestroy(&sectiondm);
2402        DMDestroy(&dm);
2403 .ve
2404 
2405 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2406 @*/
2407 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2408 {
2409   PetscBool ishdf5;
2410 
2411   PetscFunctionBegin;
2412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2413   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2414   if (!sectiondm) sectiondm = dm;
2415   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2416   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2417   /* Check consistency */
2418   {
2419     PetscSection section;
2420     PetscBool    includesConstraints;
2421     PetscInt     m, m1;
2422 
2423     PetscCall(VecGetLocalSize(vec, &m1));
2424     PetscCall(DMGetLocalSection(sectiondm, &section));
2425     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2426     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2427     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2428     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2429   }
2430   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2431   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2432   if (ishdf5) {
2433 #if defined(PETSC_HAVE_HDF5)
2434     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2435 #else
2436     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2437 #endif
2438   }
2439   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2440   PetscFunctionReturn(PETSC_SUCCESS);
2441 }
2442 
2443 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2444 {
2445   PetscBool ishdf5;
2446 
2447   PetscFunctionBegin;
2448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2449   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2451   if (ishdf5) {
2452 #if defined(PETSC_HAVE_HDF5)
2453     PetscViewerFormat format;
2454     PetscCall(PetscViewerGetFormat(viewer, &format));
2455     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2456       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2457     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2458       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2459     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2460     PetscFunctionReturn(PETSC_SUCCESS);
2461 #else
2462     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2463 #endif
2464   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2465 }
2466 
2467 /*@
2468   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2469 
2470   Collective
2471 
2472   Input Parameters:
2473 + dm     - The `DM` into which the topology is loaded
2474 - viewer - The `PetscViewer` for the saved topology
2475 
2476   Output Parameter:
2477 . 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;
2478   `NULL` if unneeded
2479 
2480   Level: advanced
2481 
2482 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2483           `PetscViewer`, `PetscSF`
2484 @*/
2485 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2486 {
2487   PetscBool ishdf5;
2488 
2489   PetscFunctionBegin;
2490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2491   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2492   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2493   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2494   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2495   if (ishdf5) {
2496 #if defined(PETSC_HAVE_HDF5)
2497     PetscViewerFormat format;
2498 
2499     PetscCall(PetscViewerGetFormat(viewer, &format));
2500     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2501     PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 /*@
2511   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2512 
2513   Collective
2514 
2515   Input Parameters:
2516 + dm                   - The `DM` into which the coordinates are loaded
2517 . viewer               - The `PetscViewer` for the saved coordinates
2518 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2519 
2520   Level: advanced
2521 
2522 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2523           `PetscSF`, `PetscViewer`
2524 @*/
2525 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2526 {
2527   PetscBool ishdf5;
2528 
2529   PetscFunctionBegin;
2530   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2531   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2532   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2534   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2535   if (ishdf5) {
2536 #if defined(PETSC_HAVE_HDF5)
2537     PetscViewerFormat format;
2538 
2539     PetscCall(PetscViewerGetFormat(viewer, &format));
2540     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2541     PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2542 #else
2543     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2544 #endif
2545   }
2546   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2547   PetscFunctionReturn(PETSC_SUCCESS);
2548 }
2549 
2550 /*@
2551   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2552 
2553   Collective
2554 
2555   Input Parameters:
2556 + dm                   - The `DM` into which the labels are loaded
2557 . viewer               - The `PetscViewer` for the saved labels
2558 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2559 
2560   Level: advanced
2561 
2562   Note:
2563   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2564 
2565 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2566           `PetscSF`, `PetscViewer`
2567 @*/
2568 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2569 {
2570   PetscBool ishdf5;
2571 
2572   PetscFunctionBegin;
2573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2574   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2575   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2577   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2578   if (ishdf5) {
2579 #if defined(PETSC_HAVE_HDF5)
2580     PetscViewerFormat format;
2581 
2582     PetscCall(PetscViewerGetFormat(viewer, &format));
2583     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2584     PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2585 #else
2586     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2587 #endif
2588   }
2589   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2590   PetscFunctionReturn(PETSC_SUCCESS);
2591 }
2592 
2593 /*@
2594   DMPlexSectionLoad - Loads section into a `DMPLEX`
2595 
2596   Collective
2597 
2598   Input Parameters:
2599 + dm                   - The `DM` that represents the topology
2600 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2601 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2602 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2603 
2604   Output Parameters:
2605 + 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)
2606 - 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)
2607 
2608   Level: advanced
2609 
2610   Notes:
2611   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.
2612 
2613   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.
2614 
2615   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.
2616 
2617   Example using 2 processes:
2618 .vb
2619   NX (number of points on dm): 4
2620   sectionA                   : the on-disk section
2621   vecA                       : a vector associated with sectionA
2622   sectionB                   : sectiondm's local section constructed in this function
2623   vecB (local)               : a vector associated with sectiondm's local section
2624   vecB (global)              : a vector associated with sectiondm's global section
2625 
2626                                      rank 0    rank 1
2627   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2628   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2629   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2630   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2631   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2632   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2633   sectionB->atlasDof             :     1 0 1 | 1 3
2634   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2635   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2636   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2637 .ve
2638   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2639 
2640 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2641 @*/
2642 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2643 {
2644   PetscBool ishdf5;
2645 
2646   PetscFunctionBegin;
2647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2648   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2649   if (!sectiondm) sectiondm = dm;
2650   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2651   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2652   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2653   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2655   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2656   if (ishdf5) {
2657 #if defined(PETSC_HAVE_HDF5)
2658     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2659 #else
2660     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2661 #endif
2662   }
2663   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2664   PetscFunctionReturn(PETSC_SUCCESS);
2665 }
2666 
2667 /*@
2668   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2669 
2670   Collective
2671 
2672   Input Parameters:
2673 + dm        - The `DM` that represents the topology
2674 . viewer    - The `PetscViewer` that represents the on-disk vector data
2675 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2676 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2677 - vec       - The global vector to set values of
2678 
2679   Level: advanced
2680 
2681   Notes:
2682   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.
2683 
2684   Calling sequence:
2685 .vb
2686        DMCreate(PETSC_COMM_WORLD, &dm);
2687        DMSetType(dm, DMPLEX);
2688        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2689        DMPlexTopologyLoad(dm, viewer, &sfX);
2690        DMClone(dm, &sectiondm);
2691        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2692        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2693        DMGetGlobalVector(sectiondm, &vec);
2694        PetscObjectSetName((PetscObject)vec, "vec_name");
2695        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2696        DMRestoreGlobalVector(sectiondm, &vec);
2697        PetscSFDestroy(&gsf);
2698        PetscSFDestroy(&sfX);
2699        DMDestroy(&sectiondm);
2700        DMDestroy(&dm);
2701 .ve
2702 
2703 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2704           `PetscSF`, `PetscViewer`
2705 @*/
2706 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2707 {
2708   PetscBool ishdf5;
2709 
2710   PetscFunctionBegin;
2711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2712   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2713   if (!sectiondm) sectiondm = dm;
2714   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2715   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2716   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2717   /* Check consistency */
2718   {
2719     PetscSection section;
2720     PetscBool    includesConstraints;
2721     PetscInt     m, m1;
2722 
2723     PetscCall(VecGetLocalSize(vec, &m1));
2724     PetscCall(DMGetGlobalSection(sectiondm, &section));
2725     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2726     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2727     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2728     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2729   }
2730   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2731   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2732   if (ishdf5) {
2733 #if defined(PETSC_HAVE_HDF5)
2734     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2735 #else
2736     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2737 #endif
2738   }
2739   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2740   PetscFunctionReturn(PETSC_SUCCESS);
2741 }
2742 
2743 /*@
2744   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2745 
2746   Collective
2747 
2748   Input Parameters:
2749 + dm        - The `DM` that represents the topology
2750 . viewer    - The `PetscViewer` that represents the on-disk vector data
2751 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2752 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2753 - vec       - The local vector to set values of
2754 
2755   Level: advanced
2756 
2757   Notes:
2758   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.
2759 
2760   Calling sequence:
2761 .vb
2762        DMCreate(PETSC_COMM_WORLD, &dm);
2763        DMSetType(dm, DMPLEX);
2764        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2765        DMPlexTopologyLoad(dm, viewer, &sfX);
2766        DMClone(dm, &sectiondm);
2767        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2768        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2769        DMGetLocalVector(sectiondm, &vec);
2770        PetscObjectSetName((PetscObject)vec, "vec_name");
2771        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2772        DMRestoreLocalVector(sectiondm, &vec);
2773        PetscSFDestroy(&lsf);
2774        PetscSFDestroy(&sfX);
2775        DMDestroy(&sectiondm);
2776        DMDestroy(&dm);
2777 .ve
2778 
2779 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2780           `PetscSF`, `PetscViewer`
2781 @*/
2782 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2783 {
2784   PetscBool ishdf5;
2785 
2786   PetscFunctionBegin;
2787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2788   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2789   if (!sectiondm) sectiondm = dm;
2790   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2791   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2792   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2793   /* Check consistency */
2794   {
2795     PetscSection section;
2796     PetscBool    includesConstraints;
2797     PetscInt     m, m1;
2798 
2799     PetscCall(VecGetLocalSize(vec, &m1));
2800     PetscCall(DMGetLocalSection(sectiondm, &section));
2801     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2802     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2803     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2804     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2805   }
2806   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2807   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2808   if (ishdf5) {
2809 #if defined(PETSC_HAVE_HDF5)
2810     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2811 #else
2812     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2813 #endif
2814   }
2815   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2816   PetscFunctionReturn(PETSC_SUCCESS);
2817 }
2818 
2819 PetscErrorCode DMDestroy_Plex(DM dm)
2820 {
2821   DM_Plex *mesh = (DM_Plex *)dm->data;
2822 
2823   PetscFunctionBegin;
2824   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2825   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2826   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2827   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBounds_C", NULL));
2828   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2829   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2830   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2837   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2838   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2839   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2840   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2841   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2842   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2843   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2844   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2845   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2846   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2847   PetscCall(PetscFree(mesh->cones));
2848   PetscCall(PetscFree(mesh->coneOrientations));
2849   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2850   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2851   PetscCall(PetscFree(mesh->supports));
2852   PetscCall(PetscFree(mesh->cellTypes));
2853   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2854   PetscCall(PetscFree(mesh->tetgenOpts));
2855   PetscCall(PetscFree(mesh->triangleOpts));
2856   PetscCall(PetscFree(mesh->transformType));
2857   PetscCall(PetscFree(mesh->distributionName));
2858   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2859   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2860   PetscCall(ISDestroy(&mesh->subpointIS));
2861   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2862   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2863   if (mesh->periodic.face_sfs) {
2864     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2865     PetscCall(PetscFree(mesh->periodic.face_sfs));
2866   }
2867   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2868   if (mesh->periodic.periodic_points) {
2869     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2870     PetscCall(PetscFree(mesh->periodic.periodic_points));
2871   }
2872   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2873   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2874   PetscCall(ISDestroy(&mesh->anchorIS));
2875   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2876   PetscCall(PetscFree(mesh->parents));
2877   PetscCall(PetscFree(mesh->childIDs));
2878   PetscCall(PetscSectionDestroy(&mesh->childSection));
2879   PetscCall(PetscFree(mesh->children));
2880   PetscCall(DMDestroy(&mesh->referenceTree));
2881   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2882   PetscCall(PetscFree(mesh->neighbors));
2883   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2884   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2885   PetscCall(DMPlexTransformDestroy(&mesh->transform));
2886   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2887   PetscCall(PetscFree(mesh));
2888   PetscFunctionReturn(PETSC_SUCCESS);
2889 }
2890 
2891 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2892 {
2893   PetscSection           sectionGlobal, sectionLocal;
2894   PetscInt               bs = -1, mbs;
2895   PetscInt               localSize, localStart = 0;
2896   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2897   MatType                mtype;
2898   ISLocalToGlobalMapping ltog;
2899 
2900   PetscFunctionBegin;
2901   PetscCall(MatInitializePackage());
2902   mtype = dm->mattype;
2903   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2904   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2905   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2906   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2907   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2908   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2909   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2910   PetscCall(MatSetType(*J, mtype));
2911   PetscCall(MatSetFromOptions(*J));
2912   PetscCall(MatGetBlockSize(*J, &mbs));
2913   if (mbs > 1) bs = mbs;
2914   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2915   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2916   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2917   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2918   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2919   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2920   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2921   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2922   if (!isShell) {
2923     // There are three states with pblocks, since block starts can have no dofs:
2924     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2925     // TRUE)    Block Start: The first entry in a block has been added
2926     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2927     PetscBT         blst;
2928     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2929     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2930     const PetscInt *perm       = NULL;
2931     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2932     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2933 
2934     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2935     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2936     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2937 
2938     PetscCall(PetscCalloc1(localSize, &pblocks));
2939     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2940     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2941     // We need to process in the permuted order to get block sizes right
2942     for (PetscInt point = pStart; point < pEnd; ++point) {
2943       const PetscInt p = perm ? perm[point] : point;
2944 
2945       switch (dm->blocking_type) {
2946       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2947         PetscInt bdof, offset;
2948 
2949         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2950         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2951         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2952         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2953         if (dof > 0) {
2954           // State change
2955           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2956           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2957 
2958           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2959           // Signal block concatenation
2960           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2961         }
2962         dof  = dof < 0 ? -(dof + 1) : dof;
2963         bdof = cdof && (dof - cdof) ? 1 : dof;
2964         if (dof) {
2965           if (bs < 0) {
2966             bs = bdof;
2967           } else if (bs != bdof) {
2968             bs = 1;
2969           }
2970         }
2971       } break;
2972       case DM_BLOCKING_FIELD_NODE: {
2973         for (PetscInt field = 0; field < num_fields; field++) {
2974           PetscInt num_comp, bdof, offset;
2975           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2976           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2977           if (dof < 0) continue;
2978           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2979           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2980           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);
2981           PetscInt num_nodes = dof / num_comp;
2982           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2983           // Handle possibly constant block size (unlikely)
2984           bdof = cdof && (dof - cdof) ? 1 : dof;
2985           if (dof) {
2986             if (bs < 0) {
2987               bs = bdof;
2988             } else if (bs != bdof) {
2989               bs = 1;
2990             }
2991           }
2992         }
2993       } break;
2994       }
2995     }
2996     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2997     /* Must have same blocksize on all procs (some might have no points) */
2998     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2999     bsLocal[1] = bs;
3000     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
3001     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
3002     else bs = bsMinMax[0];
3003     bs = PetscMax(1, bs);
3004     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
3005     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
3006       PetscCall(MatSetBlockSize(*J, bs));
3007       PetscCall(MatSetUp(*J));
3008     } else {
3009       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3010       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3011       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3012     }
3013     if (pblocks) { // Consolidate blocks
3014       PetscInt nblocks = 0;
3015       pblocks[0]       = PetscAbs(pblocks[0]);
3016       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3017         if (pblocks[i] == 0) continue;
3018         // Negative block size indicates the blocks should be concatenated
3019         if (pblocks[i] < 0) {
3020           pblocks[i] = -pblocks[i];
3021           pblocks[nblocks - 1] += pblocks[i];
3022         } else {
3023           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3024         }
3025         for (PetscInt j = 1; j < pblocks[i]; j++)
3026           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);
3027       }
3028       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3029     }
3030     PetscCall(PetscFree(pblocks));
3031   }
3032   PetscCall(MatSetDM(*J, dm));
3033   PetscFunctionReturn(PETSC_SUCCESS);
3034 }
3035 
3036 /*@
3037   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3038 
3039   Not Collective
3040 
3041   Input Parameter:
3042 . dm - The `DMPLEX`
3043 
3044   Output Parameter:
3045 . subsection - The subdomain section
3046 
3047   Level: developer
3048 
3049 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3050 @*/
3051 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3052 {
3053   DM_Plex *mesh = (DM_Plex *)dm->data;
3054 
3055   PetscFunctionBegin;
3056   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3057   if (!mesh->subdomainSection) {
3058     PetscSection section;
3059     PetscSF      sf;
3060 
3061     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3062     PetscCall(DMGetLocalSection(dm, &section));
3063     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3064     PetscCall(PetscSFDestroy(&sf));
3065   }
3066   *subsection = mesh->subdomainSection;
3067   PetscFunctionReturn(PETSC_SUCCESS);
3068 }
3069 
3070 /*@
3071   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3072 
3073   Not Collective
3074 
3075   Input Parameter:
3076 . dm - The `DMPLEX`
3077 
3078   Output Parameters:
3079 + pStart - The first mesh point
3080 - pEnd   - The upper bound for mesh points
3081 
3082   Level: beginner
3083 
3084 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3085 @*/
3086 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3087 {
3088   DM_Plex *mesh = (DM_Plex *)dm->data;
3089 
3090   PetscFunctionBegin;
3091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3092   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3093   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3094   PetscFunctionReturn(PETSC_SUCCESS);
3095 }
3096 
3097 /*@
3098   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3099 
3100   Not Collective
3101 
3102   Input Parameters:
3103 + dm     - The `DMPLEX`
3104 . pStart - The first mesh point
3105 - pEnd   - The upper bound for mesh points
3106 
3107   Level: beginner
3108 
3109 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3110 @*/
3111 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3112 {
3113   DM_Plex *mesh = (DM_Plex *)dm->data;
3114 
3115   PetscFunctionBegin;
3116   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3117   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3118   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3119   PetscCall(PetscFree(mesh->cellTypes));
3120   PetscFunctionReturn(PETSC_SUCCESS);
3121 }
3122 
3123 /*@
3124   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3125 
3126   Not Collective
3127 
3128   Input Parameters:
3129 + dm - The `DMPLEX`
3130 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3131 
3132   Output Parameter:
3133 . size - The cone size for point `p`
3134 
3135   Level: beginner
3136 
3137 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3138 @*/
3139 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3140 {
3141   DM_Plex *mesh = (DM_Plex *)dm->data;
3142 
3143   PetscFunctionBegin;
3144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3145   PetscAssertPointer(size, 3);
3146   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3147   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3148   PetscFunctionReturn(PETSC_SUCCESS);
3149 }
3150 
3151 /*@
3152   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3153 
3154   Not Collective
3155 
3156   Input Parameters:
3157 + dm   - The `DMPLEX`
3158 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3159 - size - The cone size for point `p`
3160 
3161   Level: beginner
3162 
3163   Note:
3164   This should be called after `DMPlexSetChart()`.
3165 
3166 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3167 @*/
3168 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3169 {
3170   DM_Plex *mesh = (DM_Plex *)dm->data;
3171 
3172   PetscFunctionBegin;
3173   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3174   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3175   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3176   PetscFunctionReturn(PETSC_SUCCESS);
3177 }
3178 
3179 /*@C
3180   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3181 
3182   Not Collective
3183 
3184   Input Parameters:
3185 + dm - The `DMPLEX`
3186 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3187 
3188   Output Parameter:
3189 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3190 
3191   Level: beginner
3192 
3193   Fortran Notes:
3194   `cone` must be declared with
3195 .vb
3196   PetscInt, pointer :: cone(:)
3197 .ve
3198 
3199   You must call `DMPlexRestoreCone()` after you finish using the array.
3200   `DMPlexRestoreCone()` is not needed/available in C.
3201 
3202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3203 @*/
3204 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3205 {
3206   DM_Plex *mesh = (DM_Plex *)dm->data;
3207   PetscInt off;
3208 
3209   PetscFunctionBegin;
3210   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3211   PetscAssertPointer(cone, 3);
3212   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3213   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3214   PetscFunctionReturn(PETSC_SUCCESS);
3215 }
3216 
3217 /*@
3218   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3219 
3220   Not Collective
3221 
3222   Input Parameters:
3223 + dm - The `DMPLEX`
3224 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3225 
3226   Output Parameters:
3227 + pConesSection - `PetscSection` describing the layout of `pCones`
3228 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3229 
3230   Level: intermediate
3231 
3232 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3233 @*/
3234 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3235 {
3236   PetscSection cs, newcs;
3237   PetscInt    *cones;
3238   PetscInt    *newarr = NULL;
3239   PetscInt     n;
3240 
3241   PetscFunctionBegin;
3242   PetscCall(DMPlexGetCones(dm, &cones));
3243   PetscCall(DMPlexGetConeSection(dm, &cs));
3244   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3245   if (pConesSection) *pConesSection = newcs;
3246   if (pCones) {
3247     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3248     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3249   }
3250   PetscFunctionReturn(PETSC_SUCCESS);
3251 }
3252 
3253 /*@
3254   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3255 
3256   Not Collective
3257 
3258   Input Parameters:
3259 + dm     - The `DMPLEX`
3260 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3261 
3262   Output Parameter:
3263 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3264 
3265   Level: advanced
3266 
3267   Notes:
3268   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3269 
3270   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3271 
3272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3273           `DMPlexGetDepth()`, `IS`
3274 @*/
3275 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3276 {
3277   IS      *expandedPointsAll;
3278   PetscInt depth;
3279 
3280   PetscFunctionBegin;
3281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3282   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3283   PetscAssertPointer(expandedPoints, 3);
3284   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3285   *expandedPoints = expandedPointsAll[0];
3286   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3287   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3288   PetscFunctionReturn(PETSC_SUCCESS);
3289 }
3290 
3291 /*@
3292   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3293   (DAG points of depth 0, i.e., without cones).
3294 
3295   Not Collective
3296 
3297   Input Parameters:
3298 + dm     - The `DMPLEX`
3299 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3300 
3301   Output Parameters:
3302 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3303 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3304 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3305 
3306   Level: advanced
3307 
3308   Notes:
3309   Like `DMPlexGetConeTuple()` but recursive.
3310 
3311   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.
3312   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3313 
3314   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\:
3315   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3316   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3317 
3318 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3319           `DMPlexGetDepth()`, `PetscSection`, `IS`
3320 @*/
3321 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3322 {
3323   const PetscInt *arr0 = NULL, *cone = NULL;
3324   PetscInt       *arr = NULL, *newarr = NULL;
3325   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3326   IS             *expandedPoints_;
3327   PetscSection   *sections_;
3328 
3329   PetscFunctionBegin;
3330   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3331   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3332   if (depth) PetscAssertPointer(depth, 3);
3333   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3334   if (sections) PetscAssertPointer(sections, 5);
3335   PetscCall(ISGetLocalSize(points, &n));
3336   PetscCall(ISGetIndices(points, &arr0));
3337   PetscCall(DMPlexGetDepth(dm, &depth_));
3338   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3339   PetscCall(PetscCalloc1(depth_, &sections_));
3340   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3341   for (d = depth_ - 1; d >= 0; d--) {
3342     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3343     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3344     for (i = 0; i < n; i++) {
3345       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3346       if (arr[i] >= start && arr[i] < end) {
3347         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3348         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3349       } else {
3350         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3351       }
3352     }
3353     PetscCall(PetscSectionSetUp(sections_[d]));
3354     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3355     PetscCall(PetscMalloc1(newn, &newarr));
3356     for (i = 0; i < n; i++) {
3357       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3358       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3359       if (cn > 1) {
3360         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3361         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3362       } else {
3363         newarr[co] = arr[i];
3364       }
3365     }
3366     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3367     arr = newarr;
3368     n   = newn;
3369   }
3370   PetscCall(ISRestoreIndices(points, &arr0));
3371   *depth = depth_;
3372   if (expandedPoints) *expandedPoints = expandedPoints_;
3373   else {
3374     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3375     PetscCall(PetscFree(expandedPoints_));
3376   }
3377   if (sections) *sections = sections_;
3378   else {
3379     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3380     PetscCall(PetscFree(sections_));
3381   }
3382   PetscFunctionReturn(PETSC_SUCCESS);
3383 }
3384 
3385 /*@
3386   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3387 
3388   Not Collective
3389 
3390   Input Parameters:
3391 + dm     - The `DMPLEX`
3392 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3393 
3394   Output Parameters:
3395 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3396 . expandedPoints - (optional) An array of recursively expanded cones
3397 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3398 
3399   Level: advanced
3400 
3401   Note:
3402   See `DMPlexGetConeRecursive()`
3403 
3404 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3405           `DMPlexGetDepth()`, `IS`, `PetscSection`
3406 @*/
3407 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3408 {
3409   PetscInt d, depth_;
3410 
3411   PetscFunctionBegin;
3412   PetscCall(DMPlexGetDepth(dm, &depth_));
3413   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3414   if (depth) *depth = 0;
3415   if (expandedPoints) {
3416     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3417     PetscCall(PetscFree(*expandedPoints));
3418   }
3419   if (sections) {
3420     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3421     PetscCall(PetscFree(*sections));
3422   }
3423   PetscFunctionReturn(PETSC_SUCCESS);
3424 }
3425 
3426 /*@
3427   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
3428 
3429   Not Collective
3430 
3431   Input Parameters:
3432 + dm   - The `DMPLEX`
3433 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3434 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3435 
3436   Level: beginner
3437 
3438   Note:
3439   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3440 
3441 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3442 @*/
3443 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3444 {
3445   DM_Plex *mesh = (DM_Plex *)dm->data;
3446   PetscInt dof, off, c;
3447 
3448   PetscFunctionBegin;
3449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3450   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3451   if (dof) PetscAssertPointer(cone, 3);
3452   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3453   if (PetscDefined(USE_DEBUG)) {
3454     PetscInt pStart, pEnd;
3455     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3456     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);
3457     for (c = 0; c < dof; ++c) {
3458       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);
3459       mesh->cones[off + c] = cone[c];
3460     }
3461   } else {
3462     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3463   }
3464   PetscFunctionReturn(PETSC_SUCCESS);
3465 }
3466 
3467 /*@C
3468   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3469 
3470   Not Collective
3471 
3472   Input Parameters:
3473 + dm - The `DMPLEX`
3474 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3475 
3476   Output Parameter:
3477 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3478                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3479 
3480   Level: beginner
3481 
3482   Note:
3483   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3484   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3485   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3486   with the identity.
3487 
3488   Fortran Notes:
3489   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3490   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3491 
3492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3493           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3494 @*/
3495 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3496 {
3497   DM_Plex *mesh = (DM_Plex *)dm->data;
3498   PetscInt off;
3499 
3500   PetscFunctionBegin;
3501   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3502   if (PetscDefined(USE_DEBUG)) {
3503     PetscInt dof;
3504     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3505     if (dof) PetscAssertPointer(coneOrientation, 3);
3506   }
3507   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3508 
3509   *coneOrientation = &mesh->coneOrientations[off];
3510   PetscFunctionReturn(PETSC_SUCCESS);
3511 }
3512 
3513 /*@
3514   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3515 
3516   Not Collective
3517 
3518   Input Parameters:
3519 + dm              - The `DMPLEX`
3520 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3521 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3522 
3523   Level: beginner
3524 
3525   Notes:
3526   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3527 
3528   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3529 
3530 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3531 @*/
3532 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3533 {
3534   DM_Plex *mesh = (DM_Plex *)dm->data;
3535   PetscInt pStart, pEnd;
3536   PetscInt dof, off, c;
3537 
3538   PetscFunctionBegin;
3539   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3540   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3541   if (dof) PetscAssertPointer(coneOrientation, 3);
3542   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3543   if (PetscDefined(USE_DEBUG)) {
3544     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3545     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);
3546     for (c = 0; c < dof; ++c) {
3547       PetscInt cdof, o = coneOrientation[c];
3548 
3549       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3550       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);
3551       mesh->coneOrientations[off + c] = o;
3552     }
3553   } else {
3554     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3555   }
3556   PetscFunctionReturn(PETSC_SUCCESS);
3557 }
3558 
3559 /*@
3560   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3561 
3562   Not Collective
3563 
3564   Input Parameters:
3565 + dm        - The `DMPLEX`
3566 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3567 . conePos   - The local index in the cone where the point should be put
3568 - conePoint - The mesh point to insert
3569 
3570   Level: beginner
3571 
3572 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3573 @*/
3574 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3575 {
3576   DM_Plex *mesh = (DM_Plex *)dm->data;
3577   PetscInt pStart, pEnd;
3578   PetscInt dof, off;
3579 
3580   PetscFunctionBegin;
3581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3582   if (PetscDefined(USE_DEBUG)) {
3583     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3584     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);
3585     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);
3586     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3587     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);
3588   }
3589   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3590   mesh->cones[off + conePos] = conePoint;
3591   PetscFunctionReturn(PETSC_SUCCESS);
3592 }
3593 
3594 /*@
3595   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3596 
3597   Not Collective
3598 
3599   Input Parameters:
3600 + dm              - The `DMPLEX`
3601 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3602 . conePos         - The local index in the cone where the point should be put
3603 - coneOrientation - The point orientation to insert
3604 
3605   Level: beginner
3606 
3607   Note:
3608   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3609 
3610 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3611 @*/
3612 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3613 {
3614   DM_Plex *mesh = (DM_Plex *)dm->data;
3615   PetscInt pStart, pEnd;
3616   PetscInt dof, off;
3617 
3618   PetscFunctionBegin;
3619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3620   if (PetscDefined(USE_DEBUG)) {
3621     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3622     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);
3623     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3624     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);
3625   }
3626   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3627   mesh->coneOrientations[off + conePos] = coneOrientation;
3628   PetscFunctionReturn(PETSC_SUCCESS);
3629 }
3630 
3631 /*@C
3632   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3633 
3634   Not collective
3635 
3636   Input Parameters:
3637 + dm - The DMPlex
3638 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3639 
3640   Output Parameters:
3641 + cone - An array of points which are on the in-edges for point `p`
3642 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3643          integer giving the prescription for cone traversal.
3644 
3645   Level: beginner
3646 
3647   Notes:
3648   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3649   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3650   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3651   with the identity.
3652 
3653   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3654 
3655   Fortran Notes:
3656   `cone` and `ornt` must be declared with
3657 .vb
3658   PetscInt, pointer :: cone(:)
3659   PetscInt, pointer :: ornt(:)
3660 .ve
3661 
3662 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3663 @*/
3664 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3665 {
3666   DM_Plex *mesh = (DM_Plex *)dm->data;
3667 
3668   PetscFunctionBegin;
3669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3670   if (mesh->tr) {
3671     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3672   } else {
3673     PetscInt off;
3674     if (PetscDefined(USE_DEBUG)) {
3675       PetscInt dof;
3676       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3677       if (dof) {
3678         if (cone) PetscAssertPointer(cone, 3);
3679         if (ornt) PetscAssertPointer(ornt, 4);
3680       }
3681     }
3682     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3683     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3684     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3685   }
3686   PetscFunctionReturn(PETSC_SUCCESS);
3687 }
3688 
3689 /*@C
3690   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3691 
3692   Not Collective
3693 
3694   Input Parameters:
3695 + dm   - The DMPlex
3696 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3697 . cone - An array of points which are on the in-edges for point p
3698 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3699          integer giving the prescription for cone traversal.
3700 
3701   Level: beginner
3702 
3703 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3704 @*/
3705 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3706 {
3707   DM_Plex *mesh = (DM_Plex *)dm->data;
3708 
3709   PetscFunctionBegin;
3710   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3711   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3712   PetscFunctionReturn(PETSC_SUCCESS);
3713 }
3714 
3715 /*@
3716   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3717 
3718   Not Collective
3719 
3720   Input Parameters:
3721 + dm - The `DMPLEX`
3722 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3723 
3724   Output Parameter:
3725 . size - The support size for point `p`
3726 
3727   Level: beginner
3728 
3729 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3730 @*/
3731 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3732 {
3733   DM_Plex *mesh = (DM_Plex *)dm->data;
3734 
3735   PetscFunctionBegin;
3736   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3737   PetscAssertPointer(size, 3);
3738   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3739   PetscFunctionReturn(PETSC_SUCCESS);
3740 }
3741 
3742 /*@
3743   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3744 
3745   Not Collective
3746 
3747   Input Parameters:
3748 + dm   - The `DMPLEX`
3749 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3750 - size - The support size for point `p`
3751 
3752   Level: beginner
3753 
3754   Note:
3755   This should be called after `DMPlexSetChart()`.
3756 
3757 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3758 @*/
3759 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3760 {
3761   DM_Plex *mesh = (DM_Plex *)dm->data;
3762 
3763   PetscFunctionBegin;
3764   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3765   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3766   PetscFunctionReturn(PETSC_SUCCESS);
3767 }
3768 
3769 /*@C
3770   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3771 
3772   Not Collective
3773 
3774   Input Parameters:
3775 + dm - The `DMPLEX`
3776 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3777 
3778   Output Parameter:
3779 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3780 
3781   Level: beginner
3782 
3783   Fortran Notes:
3784   `support` must be declared with
3785 .vb
3786   PetscInt, pointer :: support(:)
3787 .ve
3788 
3789   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3790   `DMPlexRestoreSupport()` is not needed/available in C.
3791 
3792 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3793 @*/
3794 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3795 {
3796   DM_Plex *mesh = (DM_Plex *)dm->data;
3797   PetscInt off;
3798 
3799   PetscFunctionBegin;
3800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3801   PetscAssertPointer(support, 3);
3802   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3803   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3804   PetscFunctionReturn(PETSC_SUCCESS);
3805 }
3806 
3807 /*@
3808   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3809 
3810   Not Collective
3811 
3812   Input Parameters:
3813 + dm      - The `DMPLEX`
3814 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3815 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3816 
3817   Level: beginner
3818 
3819   Note:
3820   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3821 
3822 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3823 @*/
3824 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3825 {
3826   DM_Plex *mesh = (DM_Plex *)dm->data;
3827   PetscInt pStart, pEnd;
3828   PetscInt dof, off, c;
3829 
3830   PetscFunctionBegin;
3831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3832   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3833   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3834   if (dof) PetscAssertPointer(support, 3);
3835   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3836   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);
3837   for (c = 0; c < dof; ++c) {
3838     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);
3839     mesh->supports[off + c] = support[c];
3840   }
3841   PetscFunctionReturn(PETSC_SUCCESS);
3842 }
3843 
3844 /*@
3845   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3846 
3847   Not Collective
3848 
3849   Input Parameters:
3850 + dm           - The `DMPLEX`
3851 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3852 . supportPos   - The local index in the cone where the point should be put
3853 - supportPoint - The mesh point to insert
3854 
3855   Level: beginner
3856 
3857 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3858 @*/
3859 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3860 {
3861   DM_Plex *mesh = (DM_Plex *)dm->data;
3862   PetscInt pStart, pEnd;
3863   PetscInt dof, off;
3864 
3865   PetscFunctionBegin;
3866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3867   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3868   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3869   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3870   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);
3871   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);
3872   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);
3873   mesh->supports[off + supportPos] = supportPoint;
3874   PetscFunctionReturn(PETSC_SUCCESS);
3875 }
3876 
3877 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3878 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3879 {
3880   switch (ct) {
3881   case DM_POLYTOPE_SEGMENT:
3882     if (o == -1) return -2;
3883     break;
3884   case DM_POLYTOPE_TRIANGLE:
3885     if (o == -3) return -1;
3886     if (o == -2) return -3;
3887     if (o == -1) return -2;
3888     break;
3889   case DM_POLYTOPE_QUADRILATERAL:
3890     if (o == -4) return -2;
3891     if (o == -3) return -1;
3892     if (o == -2) return -4;
3893     if (o == -1) return -3;
3894     break;
3895   default:
3896     return o;
3897   }
3898   return o;
3899 }
3900 
3901 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3902 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3903 {
3904   switch (ct) {
3905   case DM_POLYTOPE_SEGMENT:
3906     if ((o == -2) || (o == 1)) return -1;
3907     if (o == -1) return 0;
3908     break;
3909   case DM_POLYTOPE_TRIANGLE:
3910     if (o == -3) return -2;
3911     if (o == -2) return -1;
3912     if (o == -1) return -3;
3913     break;
3914   case DM_POLYTOPE_QUADRILATERAL:
3915     if (o == -4) return -2;
3916     if (o == -3) return -1;
3917     if (o == -2) return -4;
3918     if (o == -1) return -3;
3919     break;
3920   default:
3921     return o;
3922   }
3923   return o;
3924 }
3925 
3926 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3927 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3928 {
3929   PetscInt pStart, pEnd, p;
3930 
3931   PetscFunctionBegin;
3932   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3933   for (p = pStart; p < pEnd; ++p) {
3934     const PetscInt *cone, *ornt;
3935     PetscInt        coneSize, c;
3936 
3937     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3938     PetscCall(DMPlexGetCone(dm, p, &cone));
3939     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3940     for (c = 0; c < coneSize; ++c) {
3941       DMPolytopeType ct;
3942       const PetscInt o = ornt[c];
3943 
3944       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3945       switch (ct) {
3946       case DM_POLYTOPE_SEGMENT:
3947         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3948         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3949         break;
3950       case DM_POLYTOPE_TRIANGLE:
3951         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3952         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3953         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3954         break;
3955       case DM_POLYTOPE_QUADRILATERAL:
3956         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3957         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3958         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3959         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3960         break;
3961       default:
3962         break;
3963       }
3964     }
3965   }
3966   PetscFunctionReturn(PETSC_SUCCESS);
3967 }
3968 
3969 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3970 {
3971   DM_Plex *mesh = (DM_Plex *)dm->data;
3972 
3973   PetscFunctionBeginHot;
3974   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3975     if (useCone) {
3976       PetscCall(DMPlexGetConeSize(dm, p, size));
3977       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3978     } else {
3979       PetscCall(DMPlexGetSupportSize(dm, p, size));
3980       PetscCall(DMPlexGetSupport(dm, p, arr));
3981     }
3982   } else {
3983     if (useCone) {
3984       const PetscSection s   = mesh->coneSection;
3985       const PetscInt     ps  = p - s->pStart;
3986       const PetscInt     off = s->atlasOff[ps];
3987 
3988       *size = s->atlasDof[ps];
3989       *arr  = mesh->cones + off;
3990       *ornt = mesh->coneOrientations + off;
3991     } else {
3992       const PetscSection s   = mesh->supportSection;
3993       const PetscInt     ps  = p - s->pStart;
3994       const PetscInt     off = s->atlasOff[ps];
3995 
3996       *size = s->atlasDof[ps];
3997       *arr  = mesh->supports + off;
3998     }
3999   }
4000   PetscFunctionReturn(PETSC_SUCCESS);
4001 }
4002 
4003 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
4004 {
4005   DM_Plex *mesh = (DM_Plex *)dm->data;
4006 
4007   PetscFunctionBeginHot;
4008   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4009     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4010   }
4011   PetscFunctionReturn(PETSC_SUCCESS);
4012 }
4013 
4014 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4015 {
4016   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4017   PetscInt       *closure;
4018   const PetscInt *tmp = NULL, *tmpO = NULL;
4019   PetscInt        off = 0, tmpSize, t;
4020 
4021   PetscFunctionBeginHot;
4022   if (ornt) {
4023     PetscCall(DMPlexGetCellType(dm, p, &ct));
4024     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;
4025   }
4026   if (*points) {
4027     closure = *points;
4028   } else {
4029     PetscInt maxConeSize, maxSupportSize;
4030     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4031     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4032   }
4033   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4034   if (ct == DM_POLYTOPE_UNKNOWN) {
4035     closure[off++] = p;
4036     closure[off++] = 0;
4037     for (t = 0; t < tmpSize; ++t) {
4038       closure[off++] = tmp[t];
4039       closure[off++] = tmpO ? tmpO[t] : 0;
4040     }
4041   } else {
4042     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4043 
4044     /* We assume that cells with a valid type have faces with a valid type */
4045     closure[off++] = p;
4046     closure[off++] = ornt;
4047     for (t = 0; t < tmpSize; ++t) {
4048       DMPolytopeType ft;
4049 
4050       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4051       closure[off++] = tmp[arr[t]];
4052       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4053     }
4054   }
4055   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4056   if (numPoints) *numPoints = tmpSize + 1;
4057   if (points) *points = closure;
4058   PetscFunctionReturn(PETSC_SUCCESS);
4059 }
4060 
4061 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4062 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4063 {
4064   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4065   const PetscInt *cone, *ornt;
4066   PetscInt       *pts, *closure = NULL;
4067   DMPolytopeType  ft;
4068   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4069   PetscInt        dim, coneSize, c, d, clSize, cl;
4070 
4071   PetscFunctionBeginHot;
4072   PetscCall(DMGetDimension(dm, &dim));
4073   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4074   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4075   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4076   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4077   maxSize       = PetscMax(coneSeries, supportSeries);
4078   if (*points) {
4079     pts = *points;
4080   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4081   c        = 0;
4082   pts[c++] = point;
4083   pts[c++] = o;
4084   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4085   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4086   for (cl = 0; cl < clSize * 2; cl += 2) {
4087     pts[c++] = closure[cl];
4088     pts[c++] = closure[cl + 1];
4089   }
4090   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4091   for (cl = 0; cl < clSize * 2; cl += 2) {
4092     pts[c++] = closure[cl];
4093     pts[c++] = closure[cl + 1];
4094   }
4095   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4096   for (d = 2; d < coneSize; ++d) {
4097     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4098     pts[c++] = cone[arr[d * 2 + 0]];
4099     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4100   }
4101   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4102   if (dim >= 3) {
4103     for (d = 2; d < coneSize; ++d) {
4104       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4105       const PetscInt *fcone, *fornt;
4106       PetscInt        fconeSize, fc, i;
4107 
4108       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4109       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4110       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4111       for (fc = 0; fc < fconeSize; ++fc) {
4112         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4113         const PetscInt co = farr[fc * 2 + 1];
4114 
4115         for (i = 0; i < c; i += 2)
4116           if (pts[i] == cp) break;
4117         if (i == c) {
4118           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4119           pts[c++] = cp;
4120           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4121         }
4122       }
4123       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4124     }
4125   }
4126   *numPoints = c / 2;
4127   *points    = pts;
4128   PetscFunctionReturn(PETSC_SUCCESS);
4129 }
4130 
4131 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4132 {
4133   DMPolytopeType ct;
4134   PetscInt      *closure, *fifo;
4135   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4136   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4137   PetscInt       depth, maxSize;
4138 
4139   PetscFunctionBeginHot;
4140   PetscCall(DMPlexGetDepth(dm, &depth));
4141   if (depth == 1) {
4142     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4143     PetscFunctionReturn(PETSC_SUCCESS);
4144   }
4145   PetscCall(DMPlexGetCellType(dm, p, &ct));
4146   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;
4147   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4148     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4149     PetscFunctionReturn(PETSC_SUCCESS);
4150   }
4151   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4152   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4153   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4154   maxSize       = PetscMax(coneSeries, supportSeries);
4155   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4156   if (*points) {
4157     closure = *points;
4158   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4159   closure[closureSize++] = p;
4160   closure[closureSize++] = ornt;
4161   fifo[fifoSize++]       = p;
4162   fifo[fifoSize++]       = ornt;
4163   fifo[fifoSize++]       = ct;
4164   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4165   while (fifoSize - fifoStart) {
4166     const PetscInt       q    = fifo[fifoStart++];
4167     const PetscInt       o    = fifo[fifoStart++];
4168     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4169     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4170     const PetscInt      *tmp, *tmpO = NULL;
4171     PetscInt             tmpSize, t;
4172 
4173     if (PetscDefined(USE_DEBUG)) {
4174       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4175       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);
4176     }
4177     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4178     for (t = 0; t < tmpSize; ++t) {
4179       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4180       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4181       const PetscInt cp = tmp[ip];
4182       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4183       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4184       PetscInt       c;
4185 
4186       /* Check for duplicate */
4187       for (c = 0; c < closureSize; c += 2) {
4188         if (closure[c] == cp) break;
4189       }
4190       if (c == closureSize) {
4191         closure[closureSize++] = cp;
4192         closure[closureSize++] = co;
4193         fifo[fifoSize++]       = cp;
4194         fifo[fifoSize++]       = co;
4195         fifo[fifoSize++]       = ct;
4196       }
4197     }
4198     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4199   }
4200   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4201   if (numPoints) *numPoints = closureSize / 2;
4202   if (points) *points = closure;
4203   PetscFunctionReturn(PETSC_SUCCESS);
4204 }
4205 
4206 /*@C
4207   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4208 
4209   Not Collective
4210 
4211   Input Parameters:
4212 + dm      - The `DMPLEX`
4213 . p       - The mesh point
4214 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4215 
4216   Input/Output Parameter:
4217 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4218            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4219            otherwise the provided array is used to hold the values
4220 
4221   Output Parameter:
4222 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4223 
4224   Level: beginner
4225 
4226   Note:
4227   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4228 
4229   Fortran Notes:
4230   `points` must be declared with
4231 .vb
4232   PetscInt, pointer :: points(:)
4233 .ve
4234   and is always allocated by the function.
4235 
4236   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4237 
4238 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4239 @*/
4240 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4241 {
4242   PetscFunctionBeginHot;
4243   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4244   if (numPoints) PetscAssertPointer(numPoints, 4);
4245   if (points) PetscAssertPointer(points, 5);
4246   if (PetscDefined(USE_DEBUG)) {
4247     PetscInt pStart, pEnd;
4248     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4249     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);
4250   }
4251   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4252   PetscFunctionReturn(PETSC_SUCCESS);
4253 }
4254 
4255 /*@C
4256   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4257 
4258   Not Collective
4259 
4260   Input Parameters:
4261 + dm        - The `DMPLEX`
4262 . p         - The mesh point
4263 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4264 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4265 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4266 
4267   Level: beginner
4268 
4269   Note:
4270   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4271 
4272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4273 @*/
4274 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4275 {
4276   PetscFunctionBeginHot;
4277   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4278   if (numPoints) *numPoints = 0;
4279   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4280   PetscFunctionReturn(PETSC_SUCCESS);
4281 }
4282 
4283 /*@
4284   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4285 
4286   Not Collective
4287 
4288   Input Parameter:
4289 . dm - The `DMPLEX`
4290 
4291   Output Parameters:
4292 + maxConeSize    - The maximum number of in-edges
4293 - maxSupportSize - The maximum number of out-edges
4294 
4295   Level: beginner
4296 
4297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4298 @*/
4299 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4300 {
4301   DM_Plex *mesh = (DM_Plex *)dm->data;
4302 
4303   PetscFunctionBegin;
4304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4305   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4306   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4307   PetscFunctionReturn(PETSC_SUCCESS);
4308 }
4309 
4310 PetscErrorCode DMSetUp_Plex(DM dm)
4311 {
4312   DM_Plex *mesh = (DM_Plex *)dm->data;
4313   PetscInt size, maxSupportSize;
4314 
4315   PetscFunctionBegin;
4316   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4317   PetscCall(PetscSectionSetUp(mesh->coneSection));
4318   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4319   PetscCall(PetscMalloc1(size, &mesh->cones));
4320   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4321   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4322   if (maxSupportSize) {
4323     PetscCall(PetscSectionSetUp(mesh->supportSection));
4324     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4325     PetscCall(PetscMalloc1(size, &mesh->supports));
4326   }
4327   PetscFunctionReturn(PETSC_SUCCESS);
4328 }
4329 
4330 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4331 {
4332   PetscFunctionBegin;
4333   if (subdm) PetscCall(DMClone(dm, subdm));
4334   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4335   if (subdm) (*subdm)->useNatural = dm->useNatural;
4336   if (dm->useNatural && dm->sfMigration) {
4337     PetscSF sfNatural;
4338 
4339     (*subdm)->sfMigration = dm->sfMigration;
4340     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4341     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4342     (*subdm)->sfNatural = sfNatural;
4343   }
4344   PetscFunctionReturn(PETSC_SUCCESS);
4345 }
4346 
4347 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4348 {
4349   PetscInt i = 0;
4350 
4351   PetscFunctionBegin;
4352   PetscCall(DMClone(dms[0], superdm));
4353   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4354   (*superdm)->useNatural = PETSC_FALSE;
4355   for (i = 0; i < len; i++) {
4356     if (dms[i]->useNatural && dms[i]->sfMigration) {
4357       PetscSF sfNatural;
4358 
4359       (*superdm)->sfMigration = dms[i]->sfMigration;
4360       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4361       (*superdm)->useNatural = PETSC_TRUE;
4362       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4363       (*superdm)->sfNatural = sfNatural;
4364       break;
4365     }
4366   }
4367   PetscFunctionReturn(PETSC_SUCCESS);
4368 }
4369 
4370 /*@
4371   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4372 
4373   Not Collective
4374 
4375   Input Parameter:
4376 . dm - The `DMPLEX`
4377 
4378   Level: beginner
4379 
4380   Note:
4381   This should be called after all calls to `DMPlexSetCone()`
4382 
4383 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4384 @*/
4385 PetscErrorCode DMPlexSymmetrize(DM dm)
4386 {
4387   DM_Plex  *mesh = (DM_Plex *)dm->data;
4388   PetscInt *offsets;
4389   PetscInt  supportSize;
4390   PetscInt  pStart, pEnd, p;
4391 
4392   PetscFunctionBegin;
4393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4394   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4395   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4396   /* Calculate support sizes */
4397   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4398   for (p = pStart; p < pEnd; ++p) {
4399     PetscInt dof, off, c;
4400 
4401     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4402     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4403     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4404   }
4405   PetscCall(PetscSectionSetUp(mesh->supportSection));
4406   /* Calculate supports */
4407   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4408   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4409   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4410   for (p = pStart; p < pEnd; ++p) {
4411     PetscInt dof, off, c;
4412 
4413     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4414     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4415     for (c = off; c < off + dof; ++c) {
4416       const PetscInt q = mesh->cones[c];
4417       PetscInt       offS;
4418 
4419       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4420 
4421       mesh->supports[offS + offsets[q]] = p;
4422       ++offsets[q];
4423     }
4424   }
4425   PetscCall(PetscFree(offsets));
4426   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4427   PetscFunctionReturn(PETSC_SUCCESS);
4428 }
4429 
4430 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4431 {
4432   IS stratumIS;
4433 
4434   PetscFunctionBegin;
4435   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4436   if (PetscDefined(USE_DEBUG)) {
4437     PetscInt  qStart, qEnd, numLevels, level;
4438     PetscBool overlap = PETSC_FALSE;
4439     PetscCall(DMLabelGetNumValues(label, &numLevels));
4440     for (level = 0; level < numLevels; level++) {
4441       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4442       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4443         overlap = PETSC_TRUE;
4444         break;
4445       }
4446     }
4447     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);
4448   }
4449   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4450   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4451   PetscCall(ISDestroy(&stratumIS));
4452   PetscFunctionReturn(PETSC_SUCCESS);
4453 }
4454 
4455 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4456 {
4457   PetscInt *pMin, *pMax;
4458   PetscInt  pStart, pEnd;
4459   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4460 
4461   PetscFunctionBegin;
4462   {
4463     DMLabel label2;
4464 
4465     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4466     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4467   }
4468   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4469   for (PetscInt p = pStart; p < pEnd; ++p) {
4470     DMPolytopeType ct;
4471 
4472     PetscCall(DMPlexGetCellType(dm, p, &ct));
4473     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4474     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4475   }
4476   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4477   for (PetscInt d = dmin; d <= dmax; ++d) {
4478     pMin[d] = PETSC_INT_MAX;
4479     pMax[d] = PETSC_INT_MIN;
4480   }
4481   for (PetscInt p = pStart; p < pEnd; ++p) {
4482     DMPolytopeType ct;
4483     PetscInt       d;
4484 
4485     PetscCall(DMPlexGetCellType(dm, p, &ct));
4486     d       = DMPolytopeTypeGetDim(ct);
4487     pMin[d] = PetscMin(p, pMin[d]);
4488     pMax[d] = PetscMax(p, pMax[d]);
4489   }
4490   for (PetscInt d = dmin; d <= dmax; ++d) {
4491     if (pMin[d] > pMax[d]) continue;
4492     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4493   }
4494   PetscCall(PetscFree2(pMin, pMax));
4495   PetscFunctionReturn(PETSC_SUCCESS);
4496 }
4497 
4498 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4499 {
4500   PetscInt pStart, pEnd;
4501   PetscInt numRoots = 0, numLeaves = 0;
4502 
4503   PetscFunctionBegin;
4504   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4505   {
4506     /* Initialize roots and count leaves */
4507     PetscInt sMin = PETSC_INT_MAX;
4508     PetscInt sMax = PETSC_INT_MIN;
4509     PetscInt coneSize, supportSize;
4510 
4511     for (PetscInt p = pStart; p < pEnd; ++p) {
4512       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4513       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4514       if (!coneSize && supportSize) {
4515         sMin = PetscMin(p, sMin);
4516         sMax = PetscMax(p, sMax);
4517         ++numRoots;
4518       } else if (!supportSize && coneSize) {
4519         ++numLeaves;
4520       } else if (!supportSize && !coneSize) {
4521         /* Isolated points */
4522         sMin = PetscMin(p, sMin);
4523         sMax = PetscMax(p, sMax);
4524       }
4525     }
4526     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4527   }
4528 
4529   if (numRoots + numLeaves == (pEnd - pStart)) {
4530     PetscInt sMin = PETSC_INT_MAX;
4531     PetscInt sMax = PETSC_INT_MIN;
4532     PetscInt coneSize, supportSize;
4533 
4534     for (PetscInt p = pStart; p < pEnd; ++p) {
4535       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4536       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4537       if (!supportSize && coneSize) {
4538         sMin = PetscMin(p, sMin);
4539         sMax = PetscMax(p, sMax);
4540       }
4541     }
4542     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4543   } else {
4544     PetscInt level = 0;
4545     PetscInt qStart, qEnd;
4546 
4547     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4548     while (qEnd > qStart) {
4549       PetscInt sMin = PETSC_INT_MAX;
4550       PetscInt sMax = PETSC_INT_MIN;
4551 
4552       for (PetscInt q = qStart; q < qEnd; ++q) {
4553         const PetscInt *support;
4554         PetscInt        supportSize;
4555 
4556         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4557         PetscCall(DMPlexGetSupport(dm, q, &support));
4558         for (PetscInt s = 0; s < supportSize; ++s) {
4559           sMin = PetscMin(support[s], sMin);
4560           sMax = PetscMax(support[s], sMax);
4561         }
4562       }
4563       PetscCall(DMLabelGetNumValues(label, &level));
4564       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4565       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4566     }
4567   }
4568   PetscFunctionReturn(PETSC_SUCCESS);
4569 }
4570 
4571 /*@
4572   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4573 
4574   Collective
4575 
4576   Input Parameter:
4577 . dm - The `DMPLEX`
4578 
4579   Level: beginner
4580 
4581   Notes:
4582   The strata group all points of the same grade, and this function calculates the strata. This
4583   grade can be seen as the height (or depth) of the point in the DAG.
4584 
4585   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4586   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4587   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4588   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4589   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4590   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4591   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4592 
4593   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4594   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4595   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
4596   to interpolate only that one (e0), so that
4597 .vb
4598   cone(c0) = {e0, v2}
4599   cone(e0) = {v0, v1}
4600 .ve
4601   If `DMPlexStratify()` is run on this mesh, it will give depths
4602 .vb
4603    depth 0 = {v0, v1, v2}
4604    depth 1 = {e0, c0}
4605 .ve
4606   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4607 
4608   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4609 
4610 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4611 @*/
4612 PetscErrorCode DMPlexStratify(DM dm)
4613 {
4614   DM_Plex  *mesh = (DM_Plex *)dm->data;
4615   DMLabel   label;
4616   PetscBool flg = PETSC_FALSE;
4617 
4618   PetscFunctionBegin;
4619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4620   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4621 
4622   // Create depth label
4623   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4624   PetscCall(DMCreateLabel(dm, "depth"));
4625   PetscCall(DMPlexGetDepthLabel(dm, &label));
4626 
4627   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4628   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4629   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4630 
4631   { /* just in case there is an empty process */
4632     PetscInt numValues, maxValues = 0, v;
4633 
4634     PetscCall(DMLabelGetNumValues(label, &numValues));
4635     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4636     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4637   }
4638   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4639   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4640   PetscFunctionReturn(PETSC_SUCCESS);
4641 }
4642 
4643 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4644 {
4645   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4646   PetscInt       dim, depth, pheight, coneSize;
4647   PetscBool      preferTensor;
4648 
4649   PetscFunctionBeginHot;
4650   PetscCall(DMGetDimension(dm, &dim));
4651   PetscCall(DMPlexGetDepth(dm, &depth));
4652   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4653   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4654   pheight = depth - pdepth;
4655   if (depth <= 1) {
4656     switch (pdepth) {
4657     case 0:
4658       ct = DM_POLYTOPE_POINT;
4659       break;
4660     case 1:
4661       switch (coneSize) {
4662       case 2:
4663         ct = DM_POLYTOPE_SEGMENT;
4664         break;
4665       case 3:
4666         ct = DM_POLYTOPE_TRIANGLE;
4667         break;
4668       case 4:
4669         switch (dim) {
4670         case 2:
4671           ct = DM_POLYTOPE_QUADRILATERAL;
4672           break;
4673         case 3:
4674           ct = DM_POLYTOPE_TETRAHEDRON;
4675           break;
4676         default:
4677           break;
4678         }
4679         break;
4680       case 5:
4681         ct = DM_POLYTOPE_PYRAMID;
4682         break;
4683       case 6:
4684         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4685         break;
4686       case 8:
4687         ct = DM_POLYTOPE_HEXAHEDRON;
4688         break;
4689       default:
4690         break;
4691       }
4692     }
4693   } else {
4694     if (pdepth == 0) {
4695       ct = DM_POLYTOPE_POINT;
4696     } else if (pheight == 0) {
4697       switch (dim) {
4698       case 1:
4699         switch (coneSize) {
4700         case 2:
4701           ct = DM_POLYTOPE_SEGMENT;
4702           break;
4703         default:
4704           break;
4705         }
4706         break;
4707       case 2:
4708         switch (coneSize) {
4709         case 3:
4710           ct = DM_POLYTOPE_TRIANGLE;
4711           break;
4712         case 4:
4713           ct = DM_POLYTOPE_QUADRILATERAL;
4714           break;
4715         default:
4716           break;
4717         }
4718         break;
4719       case 3:
4720         switch (coneSize) {
4721         case 4:
4722           ct = DM_POLYTOPE_TETRAHEDRON;
4723           break;
4724         case 5: {
4725           const PetscInt *cone;
4726           PetscInt        faceConeSize;
4727 
4728           PetscCall(DMPlexGetCone(dm, p, &cone));
4729           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4730           switch (faceConeSize) {
4731           case 3:
4732             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4733             break;
4734           case 4:
4735             ct = DM_POLYTOPE_PYRAMID;
4736             break;
4737           }
4738         } break;
4739         case 6:
4740           ct = DM_POLYTOPE_HEXAHEDRON;
4741           break;
4742         default:
4743           break;
4744         }
4745         break;
4746       default:
4747         break;
4748       }
4749     } else if (pheight > 0) {
4750       switch (coneSize) {
4751       case 2:
4752         ct = DM_POLYTOPE_SEGMENT;
4753         break;
4754       case 3:
4755         ct = DM_POLYTOPE_TRIANGLE;
4756         break;
4757       case 4:
4758         ct = DM_POLYTOPE_QUADRILATERAL;
4759         break;
4760       default:
4761         break;
4762       }
4763     }
4764   }
4765   *pt = ct;
4766   PetscFunctionReturn(PETSC_SUCCESS);
4767 }
4768 
4769 /*@
4770   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4771 
4772   Collective
4773 
4774   Input Parameter:
4775 . dm - The `DMPLEX`
4776 
4777   Level: developer
4778 
4779   Note:
4780   This function is normally called automatically when a cell type is requested. It creates an
4781   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4782   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4783 
4784   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4785 
4786 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4787 @*/
4788 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4789 {
4790   DM_Plex *mesh;
4791   DMLabel  ctLabel;
4792   PetscInt pStart, pEnd, p;
4793 
4794   PetscFunctionBegin;
4795   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4796   mesh = (DM_Plex *)dm->data;
4797   PetscCall(DMCreateLabel(dm, "celltype"));
4798   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4799   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4800   PetscCall(PetscFree(mesh->cellTypes));
4801   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4802   for (p = pStart; p < pEnd; ++p) {
4803     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4804     PetscInt       pdepth;
4805 
4806     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4807     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4808     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]);
4809     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4810     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4811   }
4812   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4813   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4814   PetscFunctionReturn(PETSC_SUCCESS);
4815 }
4816 
4817 /*@C
4818   DMPlexGetJoin - Get an array for the join of the set of points
4819 
4820   Not Collective
4821 
4822   Input Parameters:
4823 + dm        - The `DMPLEX` object
4824 . numPoints - The number of input points for the join
4825 - points    - The input points
4826 
4827   Output Parameters:
4828 + numCoveredPoints - The number of points in the join
4829 - coveredPoints    - The points in the join
4830 
4831   Level: intermediate
4832 
4833   Note:
4834   Currently, this is restricted to a single level join
4835 
4836   Fortran Notes:
4837   `converedPoints` must be declared with
4838 .vb
4839   PetscInt, pointer :: coveredPints(:)
4840 .ve
4841 
4842 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4843 @*/
4844 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4845 {
4846   DM_Plex  *mesh = (DM_Plex *)dm->data;
4847   PetscInt *join[2];
4848   PetscInt  joinSize, i = 0;
4849   PetscInt  dof, off, p, c, m;
4850   PetscInt  maxSupportSize;
4851 
4852   PetscFunctionBegin;
4853   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4854   PetscAssertPointer(points, 3);
4855   PetscAssertPointer(numCoveredPoints, 4);
4856   PetscAssertPointer(coveredPoints, 5);
4857   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4858   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4859   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4860   /* Copy in support of first point */
4861   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4862   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4863   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4864   /* Check each successive support */
4865   for (p = 1; p < numPoints; ++p) {
4866     PetscInt newJoinSize = 0;
4867 
4868     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4869     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4870     for (c = 0; c < dof; ++c) {
4871       const PetscInt point = mesh->supports[off + c];
4872 
4873       for (m = 0; m < joinSize; ++m) {
4874         if (point == join[i][m]) {
4875           join[1 - i][newJoinSize++] = point;
4876           break;
4877         }
4878       }
4879     }
4880     joinSize = newJoinSize;
4881     i        = 1 - i;
4882   }
4883   *numCoveredPoints = joinSize;
4884   *coveredPoints    = join[i];
4885   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4886   PetscFunctionReturn(PETSC_SUCCESS);
4887 }
4888 
4889 /*@C
4890   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4891 
4892   Not Collective
4893 
4894   Input Parameters:
4895 + dm        - The `DMPLEX` object
4896 . numPoints - The number of input points for the join
4897 - points    - The input points
4898 
4899   Output Parameters:
4900 + numCoveredPoints - The number of points in the join
4901 - coveredPoints    - The points in the join
4902 
4903   Level: intermediate
4904 
4905   Fortran Notes:
4906   `converedPoints` must be declared with
4907 .vb
4908   PetscInt, pointer :: coveredPoints(:)
4909 .ve
4910 
4911   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4912 
4913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4914 @*/
4915 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4916 {
4917   PetscFunctionBegin;
4918   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4919   if (points) PetscAssertPointer(points, 3);
4920   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4921   PetscAssertPointer(coveredPoints, 5);
4922   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4923   if (numCoveredPoints) *numCoveredPoints = 0;
4924   PetscFunctionReturn(PETSC_SUCCESS);
4925 }
4926 
4927 /*@C
4928   DMPlexGetFullJoin - Get an array for the join of the set of points
4929 
4930   Not Collective
4931 
4932   Input Parameters:
4933 + dm        - The `DMPLEX` object
4934 . numPoints - The number of input points for the join
4935 - points    - The input points, its length is `numPoints`
4936 
4937   Output Parameters:
4938 + numCoveredPoints - The number of points in the join
4939 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4940 
4941   Level: intermediate
4942 
4943   Fortran Notes:
4944 .vb
4945   PetscInt, pointer :: coveredPints(:)
4946 .ve
4947 
4948 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4949 @*/
4950 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4951 {
4952   PetscInt *offsets, **closures;
4953   PetscInt *join[2];
4954   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4955   PetscInt  p, d, c, m, ms;
4956 
4957   PetscFunctionBegin;
4958   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4959   PetscAssertPointer(points, 3);
4960   PetscAssertPointer(numCoveredPoints, 4);
4961   PetscAssertPointer(coveredPoints, 5);
4962 
4963   PetscCall(DMPlexGetDepth(dm, &depth));
4964   PetscCall(PetscCalloc1(numPoints, &closures));
4965   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4966   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4967   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4968   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4969   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4970 
4971   for (p = 0; p < numPoints; ++p) {
4972     PetscInt closureSize;
4973 
4974     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4975 
4976     offsets[p * (depth + 2) + 0] = 0;
4977     for (d = 0; d < depth + 1; ++d) {
4978       PetscInt pStart, pEnd, i;
4979 
4980       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4981       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4982         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4983           offsets[p * (depth + 2) + d + 1] = i;
4984           break;
4985         }
4986       }
4987       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4988     }
4989     PetscCheck(offsets[p * (depth + 2) + depth + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (depth + 2) + depth + 1], closureSize);
4990   }
4991   for (d = 0; d < depth + 1; ++d) {
4992     PetscInt dof;
4993 
4994     /* Copy in support of first point */
4995     dof = offsets[d + 1] - offsets[d];
4996     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4997     /* Check each successive cone */
4998     for (p = 1; p < numPoints && joinSize; ++p) {
4999       PetscInt newJoinSize = 0;
5000 
5001       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
5002       for (c = 0; c < dof; ++c) {
5003         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
5004 
5005         for (m = 0; m < joinSize; ++m) {
5006           if (point == join[i][m]) {
5007             join[1 - i][newJoinSize++] = point;
5008             break;
5009           }
5010         }
5011       }
5012       joinSize = newJoinSize;
5013       i        = 1 - i;
5014     }
5015     if (joinSize) break;
5016   }
5017   *numCoveredPoints = joinSize;
5018   *coveredPoints    = join[i];
5019   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5020   PetscCall(PetscFree(closures));
5021   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5022   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5023   PetscFunctionReturn(PETSC_SUCCESS);
5024 }
5025 
5026 /*@C
5027   DMPlexGetMeet - Get an array for the meet of the set of points
5028 
5029   Not Collective
5030 
5031   Input Parameters:
5032 + dm        - The `DMPLEX` object
5033 . numPoints - The number of input points for the meet
5034 - points    - The input points, of length `numPoints`
5035 
5036   Output Parameters:
5037 + numCoveringPoints - The number of points in the meet
5038 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5039 
5040   Level: intermediate
5041 
5042   Note:
5043   Currently, this is restricted to a single level meet
5044 
5045   Fortran Note:
5046   `coveringPoints` must be declared with
5047 .vb
5048   PetscInt, pointer :: coveringPoints(:)
5049 .ve
5050 
5051 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5052 @*/
5053 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5054 {
5055   DM_Plex  *mesh = (DM_Plex *)dm->data;
5056   PetscInt *meet[2];
5057   PetscInt  meetSize, i = 0;
5058   PetscInt  dof, off, p, c, m;
5059   PetscInt  maxConeSize;
5060 
5061   PetscFunctionBegin;
5062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5063   PetscAssertPointer(points, 3);
5064   PetscAssertPointer(numCoveringPoints, 4);
5065   PetscAssertPointer(coveringPoints, 5);
5066   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5067   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5068   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5069   /* Copy in cone of first point */
5070   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5071   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5072   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5073   /* Check each successive cone */
5074   for (p = 1; p < numPoints; ++p) {
5075     PetscInt newMeetSize = 0;
5076 
5077     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5078     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5079     for (c = 0; c < dof; ++c) {
5080       const PetscInt point = mesh->cones[off + c];
5081 
5082       for (m = 0; m < meetSize; ++m) {
5083         if (point == meet[i][m]) {
5084           meet[1 - i][newMeetSize++] = point;
5085           break;
5086         }
5087       }
5088     }
5089     meetSize = newMeetSize;
5090     i        = 1 - i;
5091   }
5092   *numCoveringPoints = meetSize;
5093   *coveringPoints    = meet[i];
5094   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5095   PetscFunctionReturn(PETSC_SUCCESS);
5096 }
5097 
5098 /*@C
5099   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5100 
5101   Not Collective
5102 
5103   Input Parameters:
5104 + dm        - The `DMPLEX` object
5105 . numPoints - The number of input points for the meet
5106 - points    - The input points
5107 
5108   Output Parameters:
5109 + numCoveredPoints - The number of points in the meet
5110 - coveredPoints    - The points in the meet
5111 
5112   Level: intermediate
5113 
5114 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5115 @*/
5116 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5117 {
5118   PetscFunctionBegin;
5119   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5120   if (points) PetscAssertPointer(points, 3);
5121   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5122   PetscAssertPointer(coveredPoints, 5);
5123   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5124   if (numCoveredPoints) *numCoveredPoints = 0;
5125   PetscFunctionReturn(PETSC_SUCCESS);
5126 }
5127 
5128 /*@C
5129   DMPlexGetFullMeet - Get an array for the meet of the set of points
5130 
5131   Not Collective
5132 
5133   Input Parameters:
5134 + dm        - The `DMPLEX` object
5135 . numPoints - The number of input points for the meet
5136 - points    - The input points, of length  `numPoints`
5137 
5138   Output Parameters:
5139 + numCoveredPoints - The number of points in the meet
5140 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5141 
5142   Level: intermediate
5143 
5144   Fortran Notes:
5145   `coveredPoints` must be declared with
5146 .vb
5147   PetscInt, pointer :: coveredPoints(:)
5148 .ve
5149 
5150 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5151 @*/
5152 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5153 {
5154   PetscInt *offsets, **closures;
5155   PetscInt *meet[2];
5156   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5157   PetscInt  p, h, c, m, mc;
5158 
5159   PetscFunctionBegin;
5160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5161   PetscAssertPointer(points, 3);
5162   PetscAssertPointer(numCoveredPoints, 4);
5163   PetscAssertPointer(coveredPoints, 5);
5164 
5165   PetscCall(DMPlexGetDepth(dm, &height));
5166   PetscCall(PetscMalloc1(numPoints, &closures));
5167   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5168   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5169   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5170   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5171   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5172 
5173   for (p = 0; p < numPoints; ++p) {
5174     PetscInt closureSize;
5175 
5176     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5177 
5178     offsets[p * (height + 2) + 0] = 0;
5179     for (h = 0; h < height + 1; ++h) {
5180       PetscInt pStart, pEnd, i;
5181 
5182       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5183       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5184         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5185           offsets[p * (height + 2) + h + 1] = i;
5186           break;
5187         }
5188       }
5189       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5190     }
5191     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);
5192   }
5193   for (h = 0; h < height + 1; ++h) {
5194     PetscInt dof;
5195 
5196     /* Copy in cone of first point */
5197     dof = offsets[h + 1] - offsets[h];
5198     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5199     /* Check each successive cone */
5200     for (p = 1; p < numPoints && meetSize; ++p) {
5201       PetscInt newMeetSize = 0;
5202 
5203       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5204       for (c = 0; c < dof; ++c) {
5205         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5206 
5207         for (m = 0; m < meetSize; ++m) {
5208           if (point == meet[i][m]) {
5209             meet[1 - i][newMeetSize++] = point;
5210             break;
5211           }
5212         }
5213       }
5214       meetSize = newMeetSize;
5215       i        = 1 - i;
5216     }
5217     if (meetSize) break;
5218   }
5219   *numCoveredPoints = meetSize;
5220   *coveredPoints    = meet[i];
5221   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5222   PetscCall(PetscFree(closures));
5223   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5224   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5225   PetscFunctionReturn(PETSC_SUCCESS);
5226 }
5227 
5228 /*@
5229   DMPlexEqual - Determine if two `DM` have the same topology
5230 
5231   Not Collective
5232 
5233   Input Parameters:
5234 + dmA - A `DMPLEX` object
5235 - dmB - A `DMPLEX` object
5236 
5237   Output Parameter:
5238 . equal - `PETSC_TRUE` if the topologies are identical
5239 
5240   Level: intermediate
5241 
5242   Note:
5243   We are not solving graph isomorphism, so we do not permute.
5244 
5245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5246 @*/
5247 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5248 {
5249   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5250 
5251   PetscFunctionBegin;
5252   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5253   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5254   PetscAssertPointer(equal, 3);
5255 
5256   *equal = PETSC_FALSE;
5257   PetscCall(DMPlexGetDepth(dmA, &depth));
5258   PetscCall(DMPlexGetDepth(dmB, &depthB));
5259   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5260   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5261   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5262   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5263   for (p = pStart; p < pEnd; ++p) {
5264     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5265     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5266 
5267     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5268     PetscCall(DMPlexGetCone(dmA, p, &cone));
5269     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5270     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5271     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5272     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5273     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5274     for (c = 0; c < coneSize; ++c) {
5275       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5276       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5277     }
5278     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5279     PetscCall(DMPlexGetSupport(dmA, p, &support));
5280     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5281     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5282     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5283     for (s = 0; s < supportSize; ++s) {
5284       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5285     }
5286   }
5287   *equal = PETSC_TRUE;
5288   PetscFunctionReturn(PETSC_SUCCESS);
5289 }
5290 
5291 /*@
5292   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5293 
5294   Not Collective
5295 
5296   Input Parameters:
5297 + dm         - The `DMPLEX`
5298 . cellDim    - The cell dimension
5299 - numCorners - The number of vertices on a cell
5300 
5301   Output Parameter:
5302 . numFaceVertices - The number of vertices on a face
5303 
5304   Level: developer
5305 
5306   Note:
5307   Of course this can only work for a restricted set of symmetric shapes
5308 
5309 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5310 @*/
5311 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5312 {
5313   MPI_Comm comm;
5314 
5315   PetscFunctionBegin;
5316   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5317   PetscAssertPointer(numFaceVertices, 4);
5318   switch (cellDim) {
5319   case 0:
5320     *numFaceVertices = 0;
5321     break;
5322   case 1:
5323     *numFaceVertices = 1;
5324     break;
5325   case 2:
5326     switch (numCorners) {
5327     case 3:                 /* triangle */
5328       *numFaceVertices = 2; /* Edge has 2 vertices */
5329       break;
5330     case 4:                 /* quadrilateral */
5331       *numFaceVertices = 2; /* Edge has 2 vertices */
5332       break;
5333     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5334       *numFaceVertices = 3; /* Edge has 3 vertices */
5335       break;
5336     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5337       *numFaceVertices = 3; /* Edge has 3 vertices */
5338       break;
5339     default:
5340       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5341     }
5342     break;
5343   case 3:
5344     switch (numCorners) {
5345     case 4:                 /* tetradehdron */
5346       *numFaceVertices = 3; /* Face has 3 vertices */
5347       break;
5348     case 6:                 /* tet cohesive cells */
5349       *numFaceVertices = 4; /* Face has 4 vertices */
5350       break;
5351     case 8:                 /* hexahedron */
5352       *numFaceVertices = 4; /* Face has 4 vertices */
5353       break;
5354     case 9:                 /* tet cohesive Lagrange cells */
5355       *numFaceVertices = 6; /* Face has 6 vertices */
5356       break;
5357     case 10:                /* quadratic tetrahedron */
5358       *numFaceVertices = 6; /* Face has 6 vertices */
5359       break;
5360     case 12:                /* hex cohesive Lagrange cells */
5361       *numFaceVertices = 6; /* Face has 6 vertices */
5362       break;
5363     case 18:                /* quadratic tet cohesive Lagrange cells */
5364       *numFaceVertices = 6; /* Face has 6 vertices */
5365       break;
5366     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5367       *numFaceVertices = 9; /* Face has 9 vertices */
5368       break;
5369     default:
5370       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5371     }
5372     break;
5373   default:
5374     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5375   }
5376   PetscFunctionReturn(PETSC_SUCCESS);
5377 }
5378 
5379 /*@
5380   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5381 
5382   Not Collective
5383 
5384   Input Parameter:
5385 . dm - The `DMPLEX` object
5386 
5387   Output Parameter:
5388 . depthLabel - The `DMLabel` recording point depth
5389 
5390   Level: developer
5391 
5392 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5393 @*/
5394 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5395 {
5396   PetscFunctionBegin;
5397   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5398   PetscAssertPointer(depthLabel, 2);
5399   *depthLabel = dm->depthLabel;
5400   PetscFunctionReturn(PETSC_SUCCESS);
5401 }
5402 
5403 /*@
5404   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5405 
5406   Not Collective
5407 
5408   Input Parameter:
5409 . dm - The `DMPLEX` object
5410 
5411   Output Parameter:
5412 . depth - The number of strata (breadth first levels) in the DAG
5413 
5414   Level: developer
5415 
5416   Notes:
5417   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5418 
5419   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5420 
5421   An empty mesh gives -1.
5422 
5423 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5424 @*/
5425 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5426 {
5427   DM_Plex *mesh = (DM_Plex *)dm->data;
5428   DMLabel  label;
5429   PetscInt d = -1;
5430 
5431   PetscFunctionBegin;
5432   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5433   PetscAssertPointer(depth, 2);
5434   if (mesh->tr) {
5435     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5436   } else {
5437     PetscCall(DMPlexGetDepthLabel(dm, &label));
5438     // Allow missing depths
5439     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5440     *depth = d;
5441   }
5442   PetscFunctionReturn(PETSC_SUCCESS);
5443 }
5444 
5445 /*@
5446   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5447 
5448   Not Collective
5449 
5450   Input Parameters:
5451 + dm    - The `DMPLEX` object
5452 - depth - The requested depth
5453 
5454   Output Parameters:
5455 + start - The first point at this `depth`
5456 - end   - One beyond the last point at this `depth`
5457 
5458   Level: developer
5459 
5460   Notes:
5461   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5462   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5463   higher dimension, e.g., "edges".
5464 
5465 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5466 @*/
5467 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5468 {
5469   DM_Plex *mesh = (DM_Plex *)dm->data;
5470   DMLabel  label;
5471   PetscInt pStart, pEnd;
5472 
5473   PetscFunctionBegin;
5474   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5475   if (start) {
5476     PetscAssertPointer(start, 3);
5477     *start = 0;
5478   }
5479   if (end) {
5480     PetscAssertPointer(end, 4);
5481     *end = 0;
5482   }
5483   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5484   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5485   if (depth < 0) {
5486     if (start) *start = pStart;
5487     if (end) *end = pEnd;
5488     PetscFunctionReturn(PETSC_SUCCESS);
5489   }
5490   if (mesh->tr) {
5491     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5492   } else {
5493     PetscCall(DMPlexGetDepthLabel(dm, &label));
5494     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5495     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5496   }
5497   PetscFunctionReturn(PETSC_SUCCESS);
5498 }
5499 
5500 /*@
5501   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5502 
5503   Not Collective
5504 
5505   Input Parameters:
5506 + dm     - The `DMPLEX` object
5507 - height - The requested height
5508 
5509   Output Parameters:
5510 + start - The first point at this `height`
5511 - end   - One beyond the last point at this `height`
5512 
5513   Level: developer
5514 
5515   Notes:
5516   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5517   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5518   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5519 
5520 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5521 @*/
5522 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5523 {
5524   DMLabel  label;
5525   PetscInt depth, pStart, pEnd;
5526 
5527   PetscFunctionBegin;
5528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5529   if (start) {
5530     PetscAssertPointer(start, 3);
5531     *start = 0;
5532   }
5533   if (end) {
5534     PetscAssertPointer(end, 4);
5535     *end = 0;
5536   }
5537   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5538   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5539   if (height < 0) {
5540     if (start) *start = pStart;
5541     if (end) *end = pEnd;
5542     PetscFunctionReturn(PETSC_SUCCESS);
5543   }
5544   PetscCall(DMPlexGetDepthLabel(dm, &label));
5545   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5546   else PetscCall(DMGetDimension(dm, &depth));
5547   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5548   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5549   PetscFunctionReturn(PETSC_SUCCESS);
5550 }
5551 
5552 /*@
5553   DMPlexGetPointDepth - Get the `depth` of a given point
5554 
5555   Not Collective
5556 
5557   Input Parameters:
5558 + dm    - The `DMPLEX` object
5559 - point - The point
5560 
5561   Output Parameter:
5562 . depth - The depth of the `point`
5563 
5564   Level: intermediate
5565 
5566 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5567 @*/
5568 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5569 {
5570   PetscFunctionBegin;
5571   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5572   PetscAssertPointer(depth, 3);
5573   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5574   PetscFunctionReturn(PETSC_SUCCESS);
5575 }
5576 
5577 /*@
5578   DMPlexGetPointHeight - Get the `height` of a given point
5579 
5580   Not Collective
5581 
5582   Input Parameters:
5583 + dm    - The `DMPLEX` object
5584 - point - The point
5585 
5586   Output Parameter:
5587 . height - The height of the `point`
5588 
5589   Level: intermediate
5590 
5591 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5592 @*/
5593 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5594 {
5595   PetscInt n, pDepth;
5596 
5597   PetscFunctionBegin;
5598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5599   PetscAssertPointer(height, 3);
5600   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5601   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5602   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5603   PetscFunctionReturn(PETSC_SUCCESS);
5604 }
5605 
5606 /*@
5607   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5608 
5609   Not Collective
5610 
5611   Input Parameter:
5612 . dm - The `DMPLEX` object
5613 
5614   Output Parameter:
5615 . celltypeLabel - The `DMLabel` recording cell polytope type
5616 
5617   Level: developer
5618 
5619   Note:
5620   This function will trigger automatica computation of cell types. This can be disabled by calling
5621   `DMCreateLabel`(dm, "celltype") beforehand.
5622 
5623 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5624 @*/
5625 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5626 {
5627   PetscFunctionBegin;
5628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5629   PetscAssertPointer(celltypeLabel, 2);
5630   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5631   *celltypeLabel = dm->celltypeLabel;
5632   PetscFunctionReturn(PETSC_SUCCESS);
5633 }
5634 
5635 /*@
5636   DMPlexGetCellType - Get the polytope type of a given cell
5637 
5638   Not Collective
5639 
5640   Input Parameters:
5641 + dm   - The `DMPLEX` object
5642 - cell - The cell
5643 
5644   Output Parameter:
5645 . celltype - The polytope type of the cell
5646 
5647   Level: intermediate
5648 
5649 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5650 @*/
5651 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5652 {
5653   DM_Plex *mesh = (DM_Plex *)dm->data;
5654   DMLabel  label;
5655   PetscInt ct;
5656 
5657   PetscFunctionBegin;
5658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5659   PetscAssertPointer(celltype, 3);
5660   if (mesh->tr) {
5661     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5662   } else {
5663     PetscInt pStart, pEnd;
5664 
5665     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5666     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5667       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5668       if (pEnd <= pStart) {
5669         *celltype = DM_POLYTOPE_UNKNOWN;
5670         PetscFunctionReturn(PETSC_SUCCESS);
5671       }
5672       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5673       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5674       for (PetscInt p = pStart; p < pEnd; p++) {
5675         PetscCall(DMLabelGetValue(label, p, &ct));
5676         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5677       }
5678     }
5679     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5680     if (PetscDefined(USE_DEBUG)) {
5681       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5682       PetscCall(DMLabelGetValue(label, cell, &ct));
5683       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5684       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5685     }
5686   }
5687   PetscFunctionReturn(PETSC_SUCCESS);
5688 }
5689 
5690 /*@
5691   DMPlexSetCellType - Set the polytope type of a given cell
5692 
5693   Not Collective
5694 
5695   Input Parameters:
5696 + dm       - The `DMPLEX` object
5697 . cell     - The cell
5698 - celltype - The polytope type of the cell
5699 
5700   Level: advanced
5701 
5702   Note:
5703   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5704   is executed. This function will override the computed type. However, if automatic classification will not succeed
5705   and a user wants to manually specify all types, the classification must be disabled by calling
5706   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5707 
5708 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5709 @*/
5710 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5711 {
5712   DM_Plex *mesh = (DM_Plex *)dm->data;
5713   DMLabel  label;
5714   PetscInt pStart, pEnd;
5715 
5716   PetscFunctionBegin;
5717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5718   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5719   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5720   PetscCall(DMLabelSetValue(label, cell, celltype));
5721   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5722   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5723   PetscFunctionReturn(PETSC_SUCCESS);
5724 }
5725 
5726 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5727 {
5728   PetscSection section;
5729   PetscInt     maxHeight;
5730   const char  *prefix;
5731 
5732   PetscFunctionBegin;
5733   PetscCall(DMClone(dm, cdm));
5734   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5735   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5736   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5737   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5738   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5739   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5740   PetscCall(DMSetLocalSection(*cdm, section));
5741   PetscCall(PetscSectionDestroy(&section));
5742 
5743   PetscCall(DMSetNumFields(*cdm, 1));
5744   PetscCall(DMCreateDS(*cdm));
5745   (*cdm)->cloneOpts = PETSC_TRUE;
5746   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5747   PetscFunctionReturn(PETSC_SUCCESS);
5748 }
5749 
5750 PetscErrorCode DMCreateCellCoordinateDM_Plex(DM dm, DM *cdm)
5751 {
5752   DM           cgcdm;
5753   PetscSection section;
5754   const char  *prefix;
5755 
5756   PetscFunctionBegin;
5757   PetscCall(DMGetCoordinateDM(dm, &cgcdm));
5758   PetscCall(DMClone(cgcdm, cdm));
5759   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5760   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5761   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cellcdm_"));
5762   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5763   PetscCall(DMSetLocalSection(*cdm, section));
5764   PetscCall(PetscSectionDestroy(&section));
5765   PetscCall(DMSetNumFields(*cdm, 1));
5766   PetscCall(DMCreateDS(*cdm));
5767   (*cdm)->cloneOpts = PETSC_TRUE;
5768   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5769   PetscFunctionReturn(PETSC_SUCCESS);
5770 }
5771 
5772 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5773 {
5774   Vec coordsLocal, cellCoordsLocal;
5775   DM  coordsDM, cellCoordsDM;
5776 
5777   PetscFunctionBegin;
5778   *field = NULL;
5779   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5780   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5781   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5782   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5783   if (coordsLocal && coordsDM) {
5784     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5785     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5786   }
5787   PetscFunctionReturn(PETSC_SUCCESS);
5788 }
5789 
5790 /*@
5791   DMPlexGetConeSection - Return a section which describes the layout of cone data
5792 
5793   Not Collective
5794 
5795   Input Parameter:
5796 . dm - The `DMPLEX` object
5797 
5798   Output Parameter:
5799 . section - The `PetscSection` object
5800 
5801   Level: developer
5802 
5803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5804 @*/
5805 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5806 {
5807   DM_Plex *mesh = (DM_Plex *)dm->data;
5808 
5809   PetscFunctionBegin;
5810   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5811   if (section) *section = mesh->coneSection;
5812   PetscFunctionReturn(PETSC_SUCCESS);
5813 }
5814 
5815 /*@
5816   DMPlexGetSupportSection - Return a section which describes the layout of support data
5817 
5818   Not Collective
5819 
5820   Input Parameter:
5821 . dm - The `DMPLEX` object
5822 
5823   Output Parameter:
5824 . section - The `PetscSection` object
5825 
5826   Level: developer
5827 
5828 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5829 @*/
5830 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5831 {
5832   DM_Plex *mesh = (DM_Plex *)dm->data;
5833 
5834   PetscFunctionBegin;
5835   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5836   if (section) *section = mesh->supportSection;
5837   PetscFunctionReturn(PETSC_SUCCESS);
5838 }
5839 
5840 /*@C
5841   DMPlexGetCones - Return cone data
5842 
5843   Not Collective
5844 
5845   Input Parameter:
5846 . dm - The `DMPLEX` object
5847 
5848   Output Parameter:
5849 . cones - The cone for each point
5850 
5851   Level: developer
5852 
5853 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5854 @*/
5855 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5856 {
5857   DM_Plex *mesh = (DM_Plex *)dm->data;
5858 
5859   PetscFunctionBegin;
5860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5861   if (cones) *cones = mesh->cones;
5862   PetscFunctionReturn(PETSC_SUCCESS);
5863 }
5864 
5865 /*@C
5866   DMPlexGetConeOrientations - Return cone orientation data
5867 
5868   Not Collective
5869 
5870   Input Parameter:
5871 . dm - The `DMPLEX` object
5872 
5873   Output Parameter:
5874 . coneOrientations - The array of cone orientations for all points
5875 
5876   Level: developer
5877 
5878   Notes:
5879   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5880   as returned by `DMPlexGetConeOrientation()`.
5881 
5882   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5883 
5884 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5885 @*/
5886 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5887 {
5888   DM_Plex *mesh = (DM_Plex *)dm->data;
5889 
5890   PetscFunctionBegin;
5891   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5892   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5893   PetscFunctionReturn(PETSC_SUCCESS);
5894 }
5895 
5896 /* FEM Support */
5897 
5898 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5899 {
5900   PetscInt depth;
5901 
5902   PetscFunctionBegin;
5903   PetscCall(DMPlexGetDepth(plex, &depth));
5904   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5905   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5906   PetscFunctionReturn(PETSC_SUCCESS);
5907 }
5908 
5909 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5910 {
5911   PetscInt depth;
5912 
5913   PetscFunctionBegin;
5914   PetscCall(DMPlexGetDepth(plex, &depth));
5915   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5916   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5917   PetscFunctionReturn(PETSC_SUCCESS);
5918 }
5919 
5920 /*
5921  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5922  representing a line in the section.
5923 */
5924 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5925 {
5926   PetscObject  obj;
5927   PetscClassId id;
5928   PetscFE      fe = NULL;
5929 
5930   PetscFunctionBeginHot;
5931   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5932   PetscCall(DMGetField(dm, field, NULL, &obj));
5933   PetscCall(PetscObjectGetClassId(obj, &id));
5934   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5935 
5936   if (!fe) {
5937     /* Assume the full interpolated mesh is in the chart; lines in particular */
5938     /* An order k SEM disc has k-1 dofs on an edge */
5939     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5940     *k = *k / *Nc + 1;
5941   } else {
5942     PetscInt       dual_space_size, dim;
5943     PetscDualSpace dsp;
5944 
5945     PetscCall(DMGetDimension(dm, &dim));
5946     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5947     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5948     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5949     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5950     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5951   }
5952   PetscFunctionReturn(PETSC_SUCCESS);
5953 }
5954 
5955 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5956 {
5957   PetscFunctionBeginHot;
5958   if (tensor) {
5959     *dof = PetscPowInt(k + 1, dim);
5960   } else {
5961     switch (dim) {
5962     case 1:
5963       *dof = k + 1;
5964       break;
5965     case 2:
5966       *dof = ((k + 1) * (k + 2)) / 2;
5967       break;
5968     case 3:
5969       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5970       break;
5971     default:
5972       *dof = 0;
5973     }
5974   }
5975   PetscFunctionReturn(PETSC_SUCCESS);
5976 }
5977 
5978 /*@
5979   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5980   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5981   section provided (or the section of the `DM`).
5982 
5983   Input Parameters:
5984 + dm      - The `DM`
5985 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5986 - section - The `PetscSection` to reorder, or `NULL` for the default section
5987 
5988   Example:
5989   A typical interpolated single-quad mesh might order points as
5990 .vb
5991   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5992 
5993   v4 -- e6 -- v3
5994   |           |
5995   e7    c0    e8
5996   |           |
5997   v1 -- e5 -- v2
5998 .ve
5999 
6000   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
6001   dofs in the order of points, e.g.,
6002 .vb
6003     c0 -> [0,1,2,3]
6004     v1 -> [4]
6005     ...
6006     e5 -> [8, 9]
6007 .ve
6008 
6009   which corresponds to the dofs
6010 .vb
6011     6   10  11  7
6012     13  2   3   15
6013     12  0   1   14
6014     4   8   9   5
6015 .ve
6016 
6017   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
6018 .vb
6019   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
6020 .ve
6021 
6022   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
6023 .vb
6024    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6025 .ve
6026 
6027   Level: developer
6028 
6029   Notes:
6030   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6031   degree of the basis.
6032 
6033   This is required to run with libCEED.
6034 
6035 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6036 @*/
6037 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6038 {
6039   DMLabel   label;
6040   PetscInt  dim, depth = -1, eStart = -1, Nf;
6041   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6042 
6043   PetscFunctionBegin;
6044   PetscCall(DMGetDimension(dm, &dim));
6045   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6046   if (point < 0) {
6047     PetscInt sStart, sEnd;
6048 
6049     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6050     point = sEnd - sStart ? sStart : point;
6051   }
6052   PetscCall(DMPlexGetDepthLabel(dm, &label));
6053   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6054   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6055   if (depth == 1) {
6056     eStart = point;
6057   } else if (depth == dim) {
6058     const PetscInt *cone;
6059 
6060     PetscCall(DMPlexGetCone(dm, point, &cone));
6061     if (dim == 2) eStart = cone[0];
6062     else if (dim == 3) {
6063       const PetscInt *cone2;
6064       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6065       eStart = cone2[0];
6066     } 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);
6067   } 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);
6068 
6069   PetscCall(PetscSectionGetNumFields(section, &Nf));
6070   for (PetscInt d = 1; d <= dim; d++) {
6071     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6072     PetscInt *perm;
6073 
6074     for (f = 0; f < Nf; ++f) {
6075       PetscInt dof;
6076 
6077       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6078       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6079       if (!continuous && d < dim) continue;
6080       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6081       size += dof * Nc;
6082     }
6083     PetscCall(PetscMalloc1(size, &perm));
6084     for (f = 0; f < Nf; ++f) {
6085       switch (d) {
6086       case 1:
6087         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6088         if (!continuous && d < dim) continue;
6089         /*
6090          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6091          We want              [ vtx0; edge of length k-1; vtx1 ]
6092          */
6093         if (continuous) {
6094           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6095           for (i = 0; i < k - 1; i++)
6096             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6097           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6098           foffset = offset;
6099         } else {
6100           PetscInt dof;
6101 
6102           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6103           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6104           foffset = offset;
6105         }
6106         break;
6107       case 2:
6108         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6109         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6110         if (!continuous && d < dim) continue;
6111         /* The SEM order is
6112 
6113          v_lb, {e_b}, v_rb,
6114          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6115          v_lt, reverse {e_t}, v_rt
6116          */
6117         if (continuous) {
6118           const PetscInt of   = 0;
6119           const PetscInt oeb  = of + PetscSqr(k - 1);
6120           const PetscInt oer  = oeb + (k - 1);
6121           const PetscInt oet  = oer + (k - 1);
6122           const PetscInt oel  = oet + (k - 1);
6123           const PetscInt ovlb = oel + (k - 1);
6124           const PetscInt ovrb = ovlb + 1;
6125           const PetscInt ovrt = ovrb + 1;
6126           const PetscInt ovlt = ovrt + 1;
6127           PetscInt       o;
6128 
6129           /* bottom */
6130           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6131           for (o = oeb; o < oer; ++o)
6132             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6133           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6134           /* middle */
6135           for (i = 0; i < k - 1; ++i) {
6136             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6137             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6138               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6139             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6140           }
6141           /* top */
6142           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6143           for (o = oel - 1; o >= oet; --o)
6144             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6145           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6146           foffset = offset;
6147         } else {
6148           PetscInt dof;
6149 
6150           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6151           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6152           foffset = offset;
6153         }
6154         break;
6155       case 3:
6156         /* The original hex closure is
6157 
6158          {c,
6159          f_b, f_t, f_f, f_b, f_r, f_l,
6160          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6161          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6162          */
6163         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6164         if (!continuous && d < dim) continue;
6165         /* The SEM order is
6166          Bottom Slice
6167          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6168          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6169          v_blb, {e_bb}, v_brb,
6170 
6171          Middle Slice (j)
6172          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6173          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6174          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6175 
6176          Top Slice
6177          v_tlf, {e_tf}, v_trf,
6178          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6179          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6180          */
6181         if (continuous) {
6182           const PetscInt oc    = 0;
6183           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6184           const PetscInt oft   = ofb + PetscSqr(k - 1);
6185           const PetscInt off   = oft + PetscSqr(k - 1);
6186           const PetscInt ofk   = off + PetscSqr(k - 1);
6187           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6188           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6189           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6190           const PetscInt oebb  = oebl + (k - 1);
6191           const PetscInt oebr  = oebb + (k - 1);
6192           const PetscInt oebf  = oebr + (k - 1);
6193           const PetscInt oetf  = oebf + (k - 1);
6194           const PetscInt oetr  = oetf + (k - 1);
6195           const PetscInt oetb  = oetr + (k - 1);
6196           const PetscInt oetl  = oetb + (k - 1);
6197           const PetscInt oerf  = oetl + (k - 1);
6198           const PetscInt oelf  = oerf + (k - 1);
6199           const PetscInt oelb  = oelf + (k - 1);
6200           const PetscInt oerb  = oelb + (k - 1);
6201           const PetscInt ovblf = oerb + (k - 1);
6202           const PetscInt ovblb = ovblf + 1;
6203           const PetscInt ovbrb = ovblb + 1;
6204           const PetscInt ovbrf = ovbrb + 1;
6205           const PetscInt ovtlf = ovbrf + 1;
6206           const PetscInt ovtrf = ovtlf + 1;
6207           const PetscInt ovtrb = ovtrf + 1;
6208           const PetscInt ovtlb = ovtrb + 1;
6209           PetscInt       o, n;
6210 
6211           /* Bottom Slice */
6212           /*   bottom */
6213           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6214           for (o = oetf - 1; o >= oebf; --o)
6215             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6216           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6217           /*   middle */
6218           for (i = 0; i < k - 1; ++i) {
6219             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6220             for (n = 0; n < k - 1; ++n) {
6221               o = ofb + n * (k - 1) + i;
6222               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6223             }
6224             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6225           }
6226           /*   top */
6227           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6228           for (o = oebb; o < oebr; ++o)
6229             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6230           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6231 
6232           /* Middle Slice */
6233           for (j = 0; j < k - 1; ++j) {
6234             /*   bottom */
6235             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6236             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6237               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6238             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6239             /*   middle */
6240             for (i = 0; i < k - 1; ++i) {
6241               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6242               for (n = 0; n < k - 1; ++n)
6243                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6244               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6245             }
6246             /*   top */
6247             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6248             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6249               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6250             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6251           }
6252 
6253           /* Top Slice */
6254           /*   bottom */
6255           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6256           for (o = oetf; o < oetr; ++o)
6257             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6258           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6259           /*   middle */
6260           for (i = 0; i < k - 1; ++i) {
6261             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6262             for (n = 0; n < k - 1; ++n)
6263               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6264             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6265           }
6266           /*   top */
6267           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6268           for (o = oetl - 1; o >= oetb; --o)
6269             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6270           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6271 
6272           foffset = offset;
6273         } else {
6274           PetscInt dof;
6275 
6276           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6277           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6278           foffset = offset;
6279         }
6280         break;
6281       default:
6282         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6283       }
6284     }
6285     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6286     /* Check permutation */
6287     {
6288       PetscInt *check;
6289 
6290       PetscCall(PetscMalloc1(size, &check));
6291       for (i = 0; i < size; ++i) {
6292         check[i] = -1;
6293         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6294       }
6295       for (i = 0; i < size; ++i) check[perm[i]] = i;
6296       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6297       PetscCall(PetscFree(check));
6298     }
6299     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6300     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6301       PetscInt *loc_perm;
6302       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6303       for (PetscInt i = 0; i < size; i++) {
6304         loc_perm[i]        = perm[i];
6305         loc_perm[size + i] = size + perm[i];
6306       }
6307       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6308     }
6309   }
6310   PetscFunctionReturn(PETSC_SUCCESS);
6311 }
6312 
6313 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6314 {
6315   PetscDS  prob;
6316   PetscInt depth, Nf, h;
6317   DMLabel  label;
6318 
6319   PetscFunctionBeginHot;
6320   PetscCall(DMGetDS(dm, &prob));
6321   Nf      = prob->Nf;
6322   label   = dm->depthLabel;
6323   *dspace = NULL;
6324   if (field < Nf) {
6325     PetscObject disc = prob->disc[field];
6326 
6327     if (disc->classid == PETSCFE_CLASSID) {
6328       PetscDualSpace dsp;
6329 
6330       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6331       PetscCall(DMLabelGetNumValues(label, &depth));
6332       PetscCall(DMLabelGetValue(label, point, &h));
6333       h = depth - 1 - h;
6334       if (h) {
6335         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6336       } else {
6337         *dspace = dsp;
6338       }
6339     }
6340   }
6341   PetscFunctionReturn(PETSC_SUCCESS);
6342 }
6343 
6344 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6345 {
6346   PetscScalar       *array;
6347   const PetscScalar *vArray;
6348   const PetscInt    *cone, *coneO;
6349   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6350 
6351   PetscFunctionBeginHot;
6352   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6353   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6354   PetscCall(DMPlexGetCone(dm, point, &cone));
6355   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6356   if (!values || !*values) {
6357     if ((point >= pStart) && (point < pEnd)) {
6358       PetscInt dof;
6359 
6360       PetscCall(PetscSectionGetDof(section, point, &dof));
6361       size += dof;
6362     }
6363     for (p = 0; p < numPoints; ++p) {
6364       const PetscInt cp = cone[p];
6365       PetscInt       dof;
6366 
6367       if ((cp < pStart) || (cp >= pEnd)) continue;
6368       PetscCall(PetscSectionGetDof(section, cp, &dof));
6369       size += dof;
6370     }
6371     if (!values) {
6372       if (csize) *csize = size;
6373       PetscFunctionReturn(PETSC_SUCCESS);
6374     }
6375     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6376   } else {
6377     array = *values;
6378   }
6379   size = 0;
6380   PetscCall(VecGetArrayRead(v, &vArray));
6381   if ((point >= pStart) && (point < pEnd)) {
6382     PetscInt           dof, off, d;
6383     const PetscScalar *varr;
6384 
6385     PetscCall(PetscSectionGetDof(section, point, &dof));
6386     PetscCall(PetscSectionGetOffset(section, point, &off));
6387     varr = PetscSafePointerPlusOffset(vArray, off);
6388     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6389     size += dof;
6390   }
6391   for (p = 0; p < numPoints; ++p) {
6392     const PetscInt     cp = cone[p];
6393     PetscInt           o  = coneO[p];
6394     PetscInt           dof, off, d;
6395     const PetscScalar *varr;
6396 
6397     if ((cp < pStart) || (cp >= pEnd)) continue;
6398     PetscCall(PetscSectionGetDof(section, cp, &dof));
6399     PetscCall(PetscSectionGetOffset(section, cp, &off));
6400     varr = PetscSafePointerPlusOffset(vArray, off);
6401     if (o >= 0) {
6402       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6403     } else {
6404       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6405     }
6406     size += dof;
6407   }
6408   PetscCall(VecRestoreArrayRead(v, &vArray));
6409   if (!*values) {
6410     if (csize) *csize = size;
6411     *values = array;
6412   } else {
6413     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6414     *csize = size;
6415   }
6416   PetscFunctionReturn(PETSC_SUCCESS);
6417 }
6418 
6419 /* Compress out points not in the section */
6420 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6421 {
6422   const PetscInt np = *numPoints;
6423   PetscInt       pStart, pEnd, p, q;
6424 
6425   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6426   for (p = 0, q = 0; p < np; ++p) {
6427     const PetscInt r = points[p * 2];
6428     if ((r >= pStart) && (r < pEnd)) {
6429       points[q * 2]     = r;
6430       points[q * 2 + 1] = points[p * 2 + 1];
6431       ++q;
6432     }
6433   }
6434   *numPoints = q;
6435   return PETSC_SUCCESS;
6436 }
6437 
6438 /* Compressed closure does not apply closure permutation */
6439 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6440 {
6441   const PetscInt *cla = NULL;
6442   PetscInt        np, *pts = NULL;
6443 
6444   PetscFunctionBeginHot;
6445   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6446   if (!ornt && *clPoints) {
6447     PetscInt dof, off;
6448 
6449     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6450     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6451     PetscCall(ISGetIndices(*clPoints, &cla));
6452     np  = dof / 2;
6453     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6454   } else {
6455     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6456     PetscCall(CompressPoints_Private(section, &np, pts));
6457   }
6458   *numPoints = np;
6459   *points    = pts;
6460   *clp       = cla;
6461   PetscFunctionReturn(PETSC_SUCCESS);
6462 }
6463 
6464 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6465 {
6466   PetscFunctionBeginHot;
6467   if (!*clPoints) {
6468     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6469   } else {
6470     PetscCall(ISRestoreIndices(*clPoints, clp));
6471   }
6472   *numPoints = 0;
6473   *points    = NULL;
6474   *clSec     = NULL;
6475   *clPoints  = NULL;
6476   *clp       = NULL;
6477   PetscFunctionReturn(PETSC_SUCCESS);
6478 }
6479 
6480 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6481 {
6482   PetscInt            offset = 0, p;
6483   const PetscInt    **perms  = NULL;
6484   const PetscScalar **flips  = NULL;
6485 
6486   PetscFunctionBeginHot;
6487   *size = 0;
6488   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6489   for (p = 0; p < numPoints; p++) {
6490     const PetscInt     point = points[2 * p];
6491     const PetscInt    *perm  = perms ? perms[p] : NULL;
6492     const PetscScalar *flip  = flips ? flips[p] : NULL;
6493     PetscInt           dof, off, d;
6494     const PetscScalar *varr;
6495 
6496     PetscCall(PetscSectionGetDof(section, point, &dof));
6497     PetscCall(PetscSectionGetOffset(section, point, &off));
6498     varr = PetscSafePointerPlusOffset(vArray, off);
6499     if (clperm) {
6500       if (perm) {
6501         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6502       } else {
6503         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6504       }
6505       if (flip) {
6506         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6507       }
6508     } else {
6509       if (perm) {
6510         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6511       } else {
6512         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6513       }
6514       if (flip) {
6515         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6516       }
6517     }
6518     offset += dof;
6519   }
6520   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6521   *size = offset;
6522   PetscFunctionReturn(PETSC_SUCCESS);
6523 }
6524 
6525 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[])
6526 {
6527   PetscInt offset = 0, f;
6528 
6529   PetscFunctionBeginHot;
6530   *size = 0;
6531   for (f = 0; f < numFields; ++f) {
6532     PetscInt            p;
6533     const PetscInt    **perms = NULL;
6534     const PetscScalar **flips = NULL;
6535 
6536     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6537     for (p = 0; p < numPoints; p++) {
6538       const PetscInt     point = points[2 * p];
6539       PetscInt           fdof, foff, b;
6540       const PetscScalar *varr;
6541       const PetscInt    *perm = perms ? perms[p] : NULL;
6542       const PetscScalar *flip = flips ? flips[p] : NULL;
6543 
6544       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6545       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6546       varr = &vArray[foff];
6547       if (clperm) {
6548         if (perm) {
6549           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6550         } else {
6551           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6552         }
6553         if (flip) {
6554           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6555         }
6556       } else {
6557         if (perm) {
6558           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6559         } else {
6560           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6561         }
6562         if (flip) {
6563           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6564         }
6565       }
6566       offset += fdof;
6567     }
6568     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6569   }
6570   *size = offset;
6571   PetscFunctionReturn(PETSC_SUCCESS);
6572 }
6573 
6574 /*@C
6575   DMPlexVecGetOrientedClosure - Get an array of the values on the closure of 'point' with a given orientation, optionally applying the closure permutation.
6576 
6577   Not collective
6578 
6579   Input Parameters:
6580 + dm        - The `DM`
6581 . section   - The section describing the layout in `v`, or `NULL` to use the default section
6582 . useClPerm - Flag for whether the provided closure permutation should be applied to the values
6583 . v         - The local vector
6584 . point     - The point in the `DM`
6585 - ornt      - The orientation of the cell, an integer giving the prescription for cone traversal. Typically, this will be 0.
6586 
6587   Input/Output Parameters:
6588 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6589 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6590            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6591 
6592   Level: advanced
6593 
6594   Notes:
6595   `DMPlexVecGetOrientedClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6596   calling function. This is because `DMPlexVecGetOrientedClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6597   assembly function, and a user may already have allocated storage for this operation.
6598 
6599   Fortran Notes:
6600   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6601   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6602 
6603   `values` must be declared with
6604 .vb
6605   PetscScalar,dimension(:),pointer   :: values
6606 .ve
6607   and it will be allocated internally by PETSc to hold the values returned
6608 
6609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexGetCellCoordinates()`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`
6610 @*/
6611 PetscErrorCode DMPlexVecGetOrientedClosure(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6612 {
6613   PetscSection    clSection;
6614   IS              clPoints;
6615   PetscInt       *points = NULL;
6616   const PetscInt *clp, *perm = NULL;
6617   PetscInt        depth, numFields, numPoints, asize;
6618 
6619   PetscFunctionBeginHot;
6620   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6621   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6622   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6623   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6624   PetscCall(DMPlexGetDepth(dm, &depth));
6625   PetscCall(PetscSectionGetNumFields(section, &numFields));
6626   if (depth == 1 && numFields < 2) {
6627     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6628     PetscFunctionReturn(PETSC_SUCCESS);
6629   }
6630   /* Get points */
6631   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6632   /* Get sizes */
6633   asize = 0;
6634   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6635     PetscInt dof;
6636     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6637     asize += dof;
6638   }
6639   if (values) {
6640     const PetscScalar *vArray;
6641     PetscInt           size;
6642 
6643     if (*values) {
6644       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);
6645     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6646     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6647     PetscCall(VecGetArrayRead(v, &vArray));
6648     /* Get values */
6649     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6650     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6651     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6652     /* Cleanup array */
6653     PetscCall(VecRestoreArrayRead(v, &vArray));
6654   }
6655   if (csize) *csize = asize;
6656   /* Cleanup points */
6657   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6658   PetscFunctionReturn(PETSC_SUCCESS);
6659 }
6660 
6661 /*@C
6662   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6663 
6664   Not collective
6665 
6666   Input Parameters:
6667 + dm      - The `DM`
6668 . section - The section describing the layout in `v`, or `NULL` to use the default section
6669 . v       - The local vector
6670 - point   - The point in the `DM`
6671 
6672   Input/Output Parameters:
6673 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6674 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6675            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6676 
6677   Level: intermediate
6678 
6679   Notes:
6680   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6681   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6682   assembly function, and a user may already have allocated storage for this operation.
6683 
6684   A typical use could be
6685 .vb
6686    values = NULL;
6687    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6688    for (cl = 0; cl < clSize; ++cl) {
6689      <Compute on closure>
6690    }
6691    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6692 .ve
6693   or
6694 .vb
6695    PetscMalloc1(clMaxSize, &values);
6696    for (p = pStart; p < pEnd; ++p) {
6697      clSize = clMaxSize;
6698      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6699      for (cl = 0; cl < clSize; ++cl) {
6700        <Compute on closure>
6701      }
6702    }
6703    PetscFree(values);
6704 .ve
6705 
6706   Fortran Notes:
6707   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6708   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6709 
6710   `values` must be declared with
6711 .vb
6712   PetscScalar,dimension(:),pointer   :: values
6713 .ve
6714   and it will be allocated internally by PETSc to hold the values returned
6715 
6716 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6717 @*/
6718 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6719 {
6720   PetscFunctionBeginHot;
6721   PetscCall(DMPlexVecGetOrientedClosure(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6722   PetscFunctionReturn(PETSC_SUCCESS);
6723 }
6724 
6725 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6726 {
6727   DMLabel            depthLabel;
6728   PetscSection       clSection;
6729   IS                 clPoints;
6730   PetscScalar       *array;
6731   const PetscScalar *vArray;
6732   PetscInt          *points = NULL;
6733   const PetscInt    *clp, *perm = NULL;
6734   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6735 
6736   PetscFunctionBeginHot;
6737   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6738   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6739   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6740   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6741   PetscCall(DMPlexGetDepth(dm, &mdepth));
6742   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6743   PetscCall(PetscSectionGetNumFields(section, &numFields));
6744   if (mdepth == 1 && numFields < 2) {
6745     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6746     PetscFunctionReturn(PETSC_SUCCESS);
6747   }
6748   /* Get points */
6749   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6750   for (clsize = 0, p = 0; p < Np; p++) {
6751     PetscInt dof;
6752     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6753     clsize += dof;
6754   }
6755   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6756   /* Filter points */
6757   for (p = 0; p < numPoints * 2; p += 2) {
6758     PetscInt dep;
6759 
6760     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6761     if (dep != depth) continue;
6762     points[Np * 2 + 0] = points[p];
6763     points[Np * 2 + 1] = points[p + 1];
6764     ++Np;
6765   }
6766   /* Get array */
6767   if (!values || !*values) {
6768     PetscInt asize = 0, dof;
6769 
6770     for (p = 0; p < Np * 2; p += 2) {
6771       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6772       asize += dof;
6773     }
6774     if (!values) {
6775       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6776       if (csize) *csize = asize;
6777       PetscFunctionReturn(PETSC_SUCCESS);
6778     }
6779     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6780   } else {
6781     array = *values;
6782   }
6783   PetscCall(VecGetArrayRead(v, &vArray));
6784   /* Get values */
6785   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6786   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6787   /* Cleanup points */
6788   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6789   /* Cleanup array */
6790   PetscCall(VecRestoreArrayRead(v, &vArray));
6791   if (!*values) {
6792     if (csize) *csize = size;
6793     *values = array;
6794   } else {
6795     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6796     *csize = size;
6797   }
6798   PetscFunctionReturn(PETSC_SUCCESS);
6799 }
6800 
6801 /*@C
6802   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6803 
6804   Not collective
6805 
6806   Input Parameters:
6807 + dm      - The `DM`
6808 . section - The section describing the layout in `v`, or `NULL` to use the default section
6809 . v       - The local vector
6810 . point   - The point in the `DM`
6811 . csize   - The number of values in the closure, or `NULL`
6812 - values  - The array of values
6813 
6814   Level: intermediate
6815 
6816   Note:
6817   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6818 
6819   Fortran Note:
6820   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6821   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6822 
6823 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6824 @*/
6825 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6826 {
6827   PetscInt size = 0;
6828 
6829   PetscFunctionBegin;
6830   /* Should work without recalculating size */
6831   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6832   *values = NULL;
6833   PetscFunctionReturn(PETSC_SUCCESS);
6834 }
6835 
6836 static inline void add(PetscScalar *x, PetscScalar y)
6837 {
6838   *x += y;
6839 }
6840 static inline void insert(PetscScalar *x, PetscScalar y)
6841 {
6842   *x = y;
6843 }
6844 
6845 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[])
6846 {
6847   PetscInt        cdof;  /* The number of constraints on this point */
6848   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6849   PetscScalar    *a;
6850   PetscInt        off, cind = 0, k;
6851 
6852   PetscFunctionBegin;
6853   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6854   PetscCall(PetscSectionGetOffset(section, point, &off));
6855   a = &array[off];
6856   if (!cdof || setBC) {
6857     if (clperm) {
6858       if (perm) {
6859         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6860       } else {
6861         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6862       }
6863     } else {
6864       if (perm) {
6865         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6866       } else {
6867         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6868       }
6869     }
6870   } else {
6871     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6872     if (clperm) {
6873       if (perm) {
6874         for (k = 0; k < dof; ++k) {
6875           if ((cind < cdof) && (k == cdofs[cind])) {
6876             ++cind;
6877             continue;
6878           }
6879           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6880         }
6881       } else {
6882         for (k = 0; k < dof; ++k) {
6883           if ((cind < cdof) && (k == cdofs[cind])) {
6884             ++cind;
6885             continue;
6886           }
6887           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6888         }
6889       }
6890     } else {
6891       if (perm) {
6892         for (k = 0; k < dof; ++k) {
6893           if ((cind < cdof) && (k == cdofs[cind])) {
6894             ++cind;
6895             continue;
6896           }
6897           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6898         }
6899       } else {
6900         for (k = 0; k < dof; ++k) {
6901           if ((cind < cdof) && (k == cdofs[cind])) {
6902             ++cind;
6903             continue;
6904           }
6905           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6906         }
6907       }
6908     }
6909   }
6910   PetscFunctionReturn(PETSC_SUCCESS);
6911 }
6912 
6913 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[])
6914 {
6915   PetscInt        cdof;  /* The number of constraints on this point */
6916   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6917   PetscScalar    *a;
6918   PetscInt        off, cind = 0, k;
6919 
6920   PetscFunctionBegin;
6921   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6922   PetscCall(PetscSectionGetOffset(section, point, &off));
6923   a = &array[off];
6924   if (cdof) {
6925     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6926     if (clperm) {
6927       if (perm) {
6928         for (k = 0; k < dof; ++k) {
6929           if ((cind < cdof) && (k == cdofs[cind])) {
6930             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6931             cind++;
6932           }
6933         }
6934       } else {
6935         for (k = 0; k < dof; ++k) {
6936           if ((cind < cdof) && (k == cdofs[cind])) {
6937             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6938             cind++;
6939           }
6940         }
6941       }
6942     } else {
6943       if (perm) {
6944         for (k = 0; k < dof; ++k) {
6945           if ((cind < cdof) && (k == cdofs[cind])) {
6946             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6947             cind++;
6948           }
6949         }
6950       } else {
6951         for (k = 0; k < dof; ++k) {
6952           if ((cind < cdof) && (k == cdofs[cind])) {
6953             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6954             cind++;
6955           }
6956         }
6957       }
6958     }
6959   }
6960   PetscFunctionReturn(PETSC_SUCCESS);
6961 }
6962 
6963 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[])
6964 {
6965   PetscScalar    *a;
6966   PetscInt        fdof, foff, fcdof, foffset = *offset;
6967   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6968   PetscInt        cind = 0, b;
6969 
6970   PetscFunctionBegin;
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 || setBC) {
6976     if (clperm) {
6977       if (perm) {
6978         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6979       } else {
6980         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6981       }
6982     } else {
6983       if (perm) {
6984         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6985       } else {
6986         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6987       }
6988     }
6989   } else {
6990     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6991     if (clperm) {
6992       if (perm) {
6993         for (b = 0; b < fdof; b++) {
6994           if ((cind < fcdof) && (b == fcdofs[cind])) {
6995             ++cind;
6996             continue;
6997           }
6998           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6999         }
7000       } else {
7001         for (b = 0; b < fdof; b++) {
7002           if ((cind < fcdof) && (b == fcdofs[cind])) {
7003             ++cind;
7004             continue;
7005           }
7006           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7007         }
7008       }
7009     } else {
7010       if (perm) {
7011         for (b = 0; b < fdof; b++) {
7012           if ((cind < fcdof) && (b == fcdofs[cind])) {
7013             ++cind;
7014             continue;
7015           }
7016           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7017         }
7018       } else {
7019         for (b = 0; b < fdof; b++) {
7020           if ((cind < fcdof) && (b == fcdofs[cind])) {
7021             ++cind;
7022             continue;
7023           }
7024           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7025         }
7026       }
7027     }
7028   }
7029   *offset += fdof;
7030   PetscFunctionReturn(PETSC_SUCCESS);
7031 }
7032 
7033 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[])
7034 {
7035   PetscScalar    *a;
7036   PetscInt        fdof, foff, fcdof, foffset = *offset;
7037   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7038   PetscInt        Nc, cind = 0, ncind = 0, b;
7039   PetscBool       ncSet, fcSet;
7040 
7041   PetscFunctionBegin;
7042   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
7043   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7044   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
7045   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
7046   a = &array[foff];
7047   if (fcdof) {
7048     /* We just override fcdof and fcdofs with Ncc and comps */
7049     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7050     if (clperm) {
7051       if (perm) {
7052         if (comps) {
7053           for (b = 0; b < fdof; b++) {
7054             ncSet = fcSet = PETSC_FALSE;
7055             if (b % Nc == comps[ncind]) {
7056               ncind = (ncind + 1) % Ncc;
7057               ncSet = PETSC_TRUE;
7058             }
7059             if ((cind < fcdof) && (b == fcdofs[cind])) {
7060               ++cind;
7061               fcSet = PETSC_TRUE;
7062             }
7063             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7064           }
7065         } else {
7066           for (b = 0; b < fdof; b++) {
7067             if ((cind < fcdof) && (b == fcdofs[cind])) {
7068               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7069               ++cind;
7070             }
7071           }
7072         }
7073       } else {
7074         if (comps) {
7075           for (b = 0; b < fdof; b++) {
7076             ncSet = fcSet = PETSC_FALSE;
7077             if (b % Nc == comps[ncind]) {
7078               ncind = (ncind + 1) % Ncc;
7079               ncSet = PETSC_TRUE;
7080             }
7081             if ((cind < fcdof) && (b == fcdofs[cind])) {
7082               ++cind;
7083               fcSet = PETSC_TRUE;
7084             }
7085             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7086           }
7087         } else {
7088           for (b = 0; b < fdof; b++) {
7089             if ((cind < fcdof) && (b == fcdofs[cind])) {
7090               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7091               ++cind;
7092             }
7093           }
7094         }
7095       }
7096     } else {
7097       if (perm) {
7098         if (comps) {
7099           for (b = 0; b < fdof; b++) {
7100             ncSet = fcSet = PETSC_FALSE;
7101             if (b % Nc == comps[ncind]) {
7102               ncind = (ncind + 1) % Ncc;
7103               ncSet = PETSC_TRUE;
7104             }
7105             if ((cind < fcdof) && (b == fcdofs[cind])) {
7106               ++cind;
7107               fcSet = PETSC_TRUE;
7108             }
7109             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7110           }
7111         } else {
7112           for (b = 0; b < fdof; b++) {
7113             if ((cind < fcdof) && (b == fcdofs[cind])) {
7114               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7115               ++cind;
7116             }
7117           }
7118         }
7119       } else {
7120         if (comps) {
7121           for (b = 0; b < fdof; b++) {
7122             ncSet = fcSet = PETSC_FALSE;
7123             if (b % Nc == comps[ncind]) {
7124               ncind = (ncind + 1) % Ncc;
7125               ncSet = PETSC_TRUE;
7126             }
7127             if ((cind < fcdof) && (b == fcdofs[cind])) {
7128               ++cind;
7129               fcSet = PETSC_TRUE;
7130             }
7131             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7132           }
7133         } else {
7134           for (b = 0; b < fdof; b++) {
7135             if ((cind < fcdof) && (b == fcdofs[cind])) {
7136               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7137               ++cind;
7138             }
7139           }
7140         }
7141       }
7142     }
7143   }
7144   *offset += fdof;
7145   PetscFunctionReturn(PETSC_SUCCESS);
7146 }
7147 
7148 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7149 {
7150   PetscScalar    *array;
7151   const PetscInt *cone, *coneO;
7152   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7153 
7154   PetscFunctionBeginHot;
7155   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7156   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7157   PetscCall(DMPlexGetCone(dm, point, &cone));
7158   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7159   PetscCall(VecGetArray(v, &array));
7160   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7161     const PetscInt cp = !p ? point : cone[p - 1];
7162     const PetscInt o  = !p ? 0 : coneO[p - 1];
7163 
7164     if ((cp < pStart) || (cp >= pEnd)) {
7165       dof = 0;
7166       continue;
7167     }
7168     PetscCall(PetscSectionGetDof(section, cp, &dof));
7169     /* ADD_VALUES */
7170     {
7171       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7172       PetscScalar    *a;
7173       PetscInt        cdof, coff, cind = 0, k;
7174 
7175       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7176       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7177       a = &array[coff];
7178       if (!cdof) {
7179         if (o >= 0) {
7180           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7181         } else {
7182           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7183         }
7184       } else {
7185         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7186         if (o >= 0) {
7187           for (k = 0; k < dof; ++k) {
7188             if ((cind < cdof) && (k == cdofs[cind])) {
7189               ++cind;
7190               continue;
7191             }
7192             a[k] += values[off + k];
7193           }
7194         } else {
7195           for (k = 0; k < dof; ++k) {
7196             if ((cind < cdof) && (k == cdofs[cind])) {
7197               ++cind;
7198               continue;
7199             }
7200             a[k] += values[off + dof - k - 1];
7201           }
7202         }
7203       }
7204     }
7205   }
7206   PetscCall(VecRestoreArray(v, &array));
7207   PetscFunctionReturn(PETSC_SUCCESS);
7208 }
7209 
7210 /*@C
7211   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7212 
7213   Not collective
7214 
7215   Input Parameters:
7216 + dm      - The `DM`
7217 . section - The section describing the layout in `v`, or `NULL` to use the default section
7218 . v       - The local vector
7219 . point   - The point in the `DM`
7220 . values  - The array of values
7221 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7222             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7223 
7224   Level: intermediate
7225 
7226   Note:
7227   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7228 
7229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7230 @*/
7231 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7232 {
7233   PetscSection    clSection;
7234   IS              clPoints;
7235   PetscScalar    *array;
7236   PetscInt       *points = NULL;
7237   const PetscInt *clp, *clperm = NULL;
7238   PetscInt        depth, numFields, numPoints, p, clsize;
7239 
7240   PetscFunctionBeginHot;
7241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7242   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7243   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7244   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7245   PetscCall(DMPlexGetDepth(dm, &depth));
7246   PetscCall(PetscSectionGetNumFields(section, &numFields));
7247   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7248     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7249     PetscFunctionReturn(PETSC_SUCCESS);
7250   }
7251   /* Get points */
7252   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7253   for (clsize = 0, p = 0; p < numPoints; p++) {
7254     PetscInt dof;
7255     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7256     clsize += dof;
7257   }
7258   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7259   /* Get array */
7260   PetscCall(VecGetArray(v, &array));
7261   /* Get values */
7262   if (numFields > 0) {
7263     PetscInt offset = 0, f;
7264     for (f = 0; f < numFields; ++f) {
7265       const PetscInt    **perms = NULL;
7266       const PetscScalar **flips = NULL;
7267 
7268       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7269       switch (mode) {
7270       case INSERT_VALUES:
7271         for (p = 0; p < numPoints; p++) {
7272           const PetscInt     point = points[2 * p];
7273           const PetscInt    *perm  = perms ? perms[p] : NULL;
7274           const PetscScalar *flip  = flips ? flips[p] : NULL;
7275           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7276         }
7277         break;
7278       case INSERT_ALL_VALUES:
7279         for (p = 0; p < numPoints; p++) {
7280           const PetscInt     point = points[2 * p];
7281           const PetscInt    *perm  = perms ? perms[p] : NULL;
7282           const PetscScalar *flip  = flips ? flips[p] : NULL;
7283           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7284         }
7285         break;
7286       case INSERT_BC_VALUES:
7287         for (p = 0; p < numPoints; p++) {
7288           const PetscInt     point = points[2 * p];
7289           const PetscInt    *perm  = perms ? perms[p] : NULL;
7290           const PetscScalar *flip  = flips ? flips[p] : NULL;
7291           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7292         }
7293         break;
7294       case ADD_VALUES:
7295         for (p = 0; p < numPoints; p++) {
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(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7300         }
7301         break;
7302       case ADD_ALL_VALUES:
7303         for (p = 0; p < numPoints; p++) {
7304           const PetscInt     point = points[2 * p];
7305           const PetscInt    *perm  = perms ? perms[p] : NULL;
7306           const PetscScalar *flip  = flips ? flips[p] : NULL;
7307           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7308         }
7309         break;
7310       case ADD_BC_VALUES:
7311         for (p = 0; p < numPoints; p++) {
7312           const PetscInt     point = points[2 * p];
7313           const PetscInt    *perm  = perms ? perms[p] : NULL;
7314           const PetscScalar *flip  = flips ? flips[p] : NULL;
7315           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7316         }
7317         break;
7318       default:
7319         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7320       }
7321       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7322     }
7323   } else {
7324     PetscInt            dof, off;
7325     const PetscInt    **perms = NULL;
7326     const PetscScalar **flips = NULL;
7327 
7328     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7329     switch (mode) {
7330     case INSERT_VALUES:
7331       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7332         const PetscInt     point = points[2 * p];
7333         const PetscInt    *perm  = perms ? perms[p] : NULL;
7334         const PetscScalar *flip  = flips ? flips[p] : NULL;
7335         PetscCall(PetscSectionGetDof(section, point, &dof));
7336         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7337       }
7338       break;
7339     case INSERT_ALL_VALUES:
7340       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7341         const PetscInt     point = points[2 * p];
7342         const PetscInt    *perm  = perms ? perms[p] : NULL;
7343         const PetscScalar *flip  = flips ? flips[p] : NULL;
7344         PetscCall(PetscSectionGetDof(section, point, &dof));
7345         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7346       }
7347       break;
7348     case INSERT_BC_VALUES:
7349       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7350         const PetscInt     point = points[2 * p];
7351         const PetscInt    *perm  = perms ? perms[p] : NULL;
7352         const PetscScalar *flip  = flips ? flips[p] : NULL;
7353         PetscCall(PetscSectionGetDof(section, point, &dof));
7354         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7355       }
7356       break;
7357     case ADD_VALUES:
7358       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7359         const PetscInt     point = points[2 * p];
7360         const PetscInt    *perm  = perms ? perms[p] : NULL;
7361         const PetscScalar *flip  = flips ? flips[p] : NULL;
7362         PetscCall(PetscSectionGetDof(section, point, &dof));
7363         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7364       }
7365       break;
7366     case ADD_ALL_VALUES:
7367       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7368         const PetscInt     point = points[2 * p];
7369         const PetscInt    *perm  = perms ? perms[p] : NULL;
7370         const PetscScalar *flip  = flips ? flips[p] : NULL;
7371         PetscCall(PetscSectionGetDof(section, point, &dof));
7372         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7373       }
7374       break;
7375     case ADD_BC_VALUES:
7376       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7377         const PetscInt     point = points[2 * p];
7378         const PetscInt    *perm  = perms ? perms[p] : NULL;
7379         const PetscScalar *flip  = flips ? flips[p] : NULL;
7380         PetscCall(PetscSectionGetDof(section, point, &dof));
7381         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7382       }
7383       break;
7384     default:
7385       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7386     }
7387     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7388   }
7389   /* Cleanup points */
7390   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7391   /* Cleanup array */
7392   PetscCall(VecRestoreArray(v, &array));
7393   PetscFunctionReturn(PETSC_SUCCESS);
7394 }
7395 
7396 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7397 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7398 {
7399   PetscFunctionBegin;
7400   *contains = PETSC_TRUE;
7401   if (label) {
7402     PetscInt fdof;
7403 
7404     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7405     if (!*contains) {
7406       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7407       *offset += fdof;
7408       PetscFunctionReturn(PETSC_SUCCESS);
7409     }
7410   }
7411   PetscFunctionReturn(PETSC_SUCCESS);
7412 }
7413 
7414 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7415 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)
7416 {
7417   PetscSection    clSection;
7418   IS              clPoints;
7419   PetscScalar    *array;
7420   PetscInt       *points = NULL;
7421   const PetscInt *clp;
7422   PetscInt        numFields, numPoints, p;
7423   PetscInt        offset = 0, f;
7424 
7425   PetscFunctionBeginHot;
7426   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7427   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7428   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7429   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7430   PetscCall(PetscSectionGetNumFields(section, &numFields));
7431   /* Get points */
7432   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7433   /* Get array */
7434   PetscCall(VecGetArray(v, &array));
7435   /* Get values */
7436   for (f = 0; f < numFields; ++f) {
7437     const PetscInt    **perms = NULL;
7438     const PetscScalar **flips = NULL;
7439     PetscBool           contains;
7440 
7441     if (!fieldActive[f]) {
7442       for (p = 0; p < numPoints * 2; p += 2) {
7443         PetscInt fdof;
7444         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7445         offset += fdof;
7446       }
7447       continue;
7448     }
7449     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7450     switch (mode) {
7451     case INSERT_VALUES:
7452       for (p = 0; p < numPoints; p++) {
7453         const PetscInt     point = points[2 * p];
7454         const PetscInt    *perm  = perms ? perms[p] : NULL;
7455         const PetscScalar *flip  = flips ? flips[p] : NULL;
7456         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7457         if (!contains) continue;
7458         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7459       }
7460       break;
7461     case INSERT_ALL_VALUES:
7462       for (p = 0; p < numPoints; p++) {
7463         const PetscInt     point = points[2 * p];
7464         const PetscInt    *perm  = perms ? perms[p] : NULL;
7465         const PetscScalar *flip  = flips ? flips[p] : NULL;
7466         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7467         if (!contains) continue;
7468         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7469       }
7470       break;
7471     case INSERT_BC_VALUES:
7472       for (p = 0; p < numPoints; p++) {
7473         const PetscInt     point = points[2 * p];
7474         const PetscInt    *perm  = perms ? perms[p] : NULL;
7475         const PetscScalar *flip  = flips ? flips[p] : NULL;
7476         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7477         if (!contains) continue;
7478         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7479       }
7480       break;
7481     case ADD_VALUES:
7482       for (p = 0; p < numPoints; p++) {
7483         const PetscInt     point = points[2 * p];
7484         const PetscInt    *perm  = perms ? perms[p] : NULL;
7485         const PetscScalar *flip  = flips ? flips[p] : NULL;
7486         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7487         if (!contains) continue;
7488         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7489       }
7490       break;
7491     case ADD_ALL_VALUES:
7492       for (p = 0; p < numPoints; p++) {
7493         const PetscInt     point = points[2 * p];
7494         const PetscInt    *perm  = perms ? perms[p] : NULL;
7495         const PetscScalar *flip  = flips ? flips[p] : NULL;
7496         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7497         if (!contains) continue;
7498         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7499       }
7500       break;
7501     default:
7502       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7503     }
7504     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7505   }
7506   /* Cleanup points */
7507   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7508   /* Cleanup array */
7509   PetscCall(VecRestoreArray(v, &array));
7510   PetscFunctionReturn(PETSC_SUCCESS);
7511 }
7512 
7513 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7514 {
7515   PetscMPIInt rank;
7516   PetscInt    i, j;
7517 
7518   PetscFunctionBegin;
7519   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7520   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7521   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7522   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7523   numCIndices = numCIndices ? numCIndices : numRIndices;
7524   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7525   for (i = 0; i < numRIndices; i++) {
7526     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7527     for (j = 0; j < numCIndices; j++) {
7528 #if defined(PETSC_USE_COMPLEX)
7529       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7530 #else
7531       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7532 #endif
7533     }
7534     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7535   }
7536   PetscFunctionReturn(PETSC_SUCCESS);
7537 }
7538 
7539 /*
7540   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7541 
7542   Input Parameters:
7543 + section - The section for this data layout
7544 . islocal - Is the section (and thus indices being requested) local or global?
7545 . point   - The point contributing dofs with these indices
7546 . off     - The global offset of this point
7547 . loff    - The local offset of each field
7548 . setBC   - The flag determining whether to include indices of boundary values
7549 . perm    - A permutation of the dofs on this point, or NULL
7550 - indperm - A permutation of the entire indices array, or NULL
7551 
7552   Output Parameter:
7553 . indices - Indices for dofs on this point
7554 
7555   Level: developer
7556 
7557   Note: The indices could be local or global, depending on the value of 'off'.
7558 */
7559 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7560 {
7561   PetscInt        dof;   /* The number of unknowns on this point */
7562   PetscInt        cdof;  /* The number of constraints on this point */
7563   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7564   PetscInt        cind = 0, k;
7565 
7566   PetscFunctionBegin;
7567   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7568   PetscCall(PetscSectionGetDof(section, point, &dof));
7569   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7570   if (!cdof || setBC) {
7571     for (k = 0; k < dof; ++k) {
7572       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7573       const PetscInt ind    = indperm ? indperm[preind] : preind;
7574 
7575       indices[ind] = off + k;
7576     }
7577   } else {
7578     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7579     for (k = 0; k < dof; ++k) {
7580       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7581       const PetscInt ind    = indperm ? indperm[preind] : preind;
7582 
7583       if ((cind < cdof) && (k == cdofs[cind])) {
7584         /* Insert check for returning constrained indices */
7585         indices[ind] = -(off + k + 1);
7586         ++cind;
7587       } else {
7588         indices[ind] = off + k - (islocal ? 0 : cind);
7589       }
7590     }
7591   }
7592   *loff += dof;
7593   PetscFunctionReturn(PETSC_SUCCESS);
7594 }
7595 
7596 /*
7597  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7598 
7599  Input Parameters:
7600 + section - a section (global or local)
7601 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7602 . point - point within section
7603 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7604 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7605 . setBC - identify constrained (boundary condition) points via involution.
7606 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7607 . permsoff - offset
7608 - indperm - index permutation
7609 
7610  Output Parameter:
7611 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7612 . indices - array to hold indices (as defined by section) of each dof associated with point
7613 
7614  Notes:
7615  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7616  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7617  in the local vector.
7618 
7619  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7620  significant).  It is invalid to call with a global section and setBC=true.
7621 
7622  Developer Note:
7623  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7624  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7625  offset could be obtained from the section instead of passing it explicitly as we do now.
7626 
7627  Example:
7628  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7629  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7630  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7631  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.
7632 
7633  Level: developer
7634 */
7635 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[])
7636 {
7637   PetscInt numFields, foff, f;
7638 
7639   PetscFunctionBegin;
7640   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7641   PetscCall(PetscSectionGetNumFields(section, &numFields));
7642   for (f = 0, foff = 0; f < numFields; ++f) {
7643     PetscInt        fdof, cfdof;
7644     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7645     PetscInt        cind = 0, b;
7646     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7647 
7648     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7649     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7650     if (!cfdof || setBC) {
7651       for (b = 0; b < fdof; ++b) {
7652         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7653         const PetscInt ind    = indperm ? indperm[preind] : preind;
7654 
7655         indices[ind] = off + foff + b;
7656       }
7657     } else {
7658       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7659       for (b = 0; b < fdof; ++b) {
7660         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7661         const PetscInt ind    = indperm ? indperm[preind] : preind;
7662 
7663         if ((cind < cfdof) && (b == fcdofs[cind])) {
7664           indices[ind] = -(off + foff + b + 1);
7665           ++cind;
7666         } else {
7667           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7668         }
7669       }
7670     }
7671     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7672     foffs[f] += fdof;
7673   }
7674   PetscFunctionReturn(PETSC_SUCCESS);
7675 }
7676 
7677 /*
7678   This version believes the globalSection offsets for each field, rather than just the point offset
7679 
7680  . foffs - The offset into 'indices' for each field, since it is segregated by field
7681 
7682  Notes:
7683  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7684  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7685 */
7686 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7687 {
7688   PetscInt numFields, foff, f;
7689 
7690   PetscFunctionBegin;
7691   PetscCall(PetscSectionGetNumFields(section, &numFields));
7692   for (f = 0; f < numFields; ++f) {
7693     PetscInt        fdof, cfdof;
7694     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7695     PetscInt        cind = 0, b;
7696     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7697 
7698     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7699     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7700     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7701     if (!cfdof) {
7702       for (b = 0; b < fdof; ++b) {
7703         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7704         const PetscInt ind    = indperm ? indperm[preind] : preind;
7705 
7706         indices[ind] = foff + b;
7707       }
7708     } else {
7709       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7710       for (b = 0; b < fdof; ++b) {
7711         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7712         const PetscInt ind    = indperm ? indperm[preind] : preind;
7713 
7714         if ((cind < cfdof) && (b == fcdofs[cind])) {
7715           indices[ind] = -(foff + b + 1);
7716           ++cind;
7717         } else {
7718           indices[ind] = foff + b - cind;
7719         }
7720       }
7721     }
7722     foffs[f] += fdof;
7723   }
7724   PetscFunctionReturn(PETSC_SUCCESS);
7725 }
7726 
7727 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7728 {
7729   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7730 
7731   PetscFunctionBegin;
7732   PetscCall(PetscSectionGetNumFields(section, &numFields));
7733   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7734   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7735   for (PetscInt p = 0; p < nPoints; p++) {
7736     PetscInt     b       = pnts[2 * p];
7737     PetscInt     bSecDof = 0, bOff;
7738     PetscInt     cSecDof = 0;
7739     PetscSection indices_section;
7740 
7741     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7742     if (!bSecDof) continue;
7743     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7744     indices_section = cSecDof > 0 ? cSec : section;
7745     if (numFields) {
7746       PetscInt fStart[32], fEnd[32];
7747 
7748       fStart[0] = 0;
7749       fEnd[0]   = 0;
7750       for (PetscInt f = 0; f < numFields; f++) {
7751         PetscInt fDof = 0;
7752 
7753         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7754         fStart[f + 1] = fStart[f] + fDof;
7755         fEnd[f + 1]   = fStart[f + 1];
7756       }
7757       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7758       // only apply permutations on one side
7759       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7760       for (PetscInt f = 0; f < numFields; f++) {
7761         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7762       }
7763     } else {
7764       PetscInt bEnd = 0;
7765 
7766       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7767       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7768 
7769       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7770     }
7771   }
7772   PetscFunctionReturn(PETSC_SUCCESS);
7773 }
7774 
7775 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[])
7776 {
7777   Mat             cMat;
7778   PetscSection    aSec, cSec;
7779   IS              aIS;
7780   PetscInt        aStart = -1, aEnd = -1;
7781   PetscInt        sStart = -1, sEnd = -1;
7782   PetscInt        cStart = -1, cEnd = -1;
7783   const PetscInt *anchors;
7784   PetscInt        numFields, p;
7785   PetscInt        newNumPoints = 0, newNumIndices = 0;
7786   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7787   PetscInt        oldOffsets[32];
7788   PetscInt        newOffsets[32];
7789   PetscInt        oldOffsetsCopy[32];
7790   PetscInt        newOffsetsCopy[32];
7791   PetscScalar    *modMat         = NULL;
7792   PetscBool       anyConstrained = PETSC_FALSE;
7793 
7794   PetscFunctionBegin;
7795   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7796   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7797   PetscCall(PetscSectionGetNumFields(section, &numFields));
7798 
7799   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7800   /* if there are point-to-point constraints */
7801   if (aSec) {
7802     PetscCall(PetscArrayzero(newOffsets, 32));
7803     PetscCall(PetscArrayzero(oldOffsets, 32));
7804     PetscCall(ISGetIndices(aIS, &anchors));
7805     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7806     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7807     /* figure out how many points are going to be in the new element matrix
7808      * (we allow double counting, because it's all just going to be summed
7809      * into the global matrix anyway) */
7810     for (p = 0; p < 2 * numPoints; p += 2) {
7811       PetscInt b    = points[p];
7812       PetscInt bDof = 0, bSecDof = 0;
7813 
7814       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7815       if (!bSecDof) continue;
7816 
7817       for (PetscInt f = 0; f < numFields; f++) {
7818         PetscInt fDof = 0;
7819 
7820         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7821         oldOffsets[f + 1] += fDof;
7822       }
7823       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7824       if (bDof) {
7825         /* this point is constrained */
7826         /* it is going to be replaced by its anchors */
7827         PetscInt bOff, q;
7828 
7829         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7830         for (q = 0; q < bDof; q++) {
7831           PetscInt a    = anchors[bOff + q];
7832           PetscInt aDof = 0;
7833 
7834           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7835           if (aDof) {
7836             anyConstrained = PETSC_TRUE;
7837             newNumPoints += 1;
7838           }
7839           newNumIndices += aDof;
7840           for (PetscInt f = 0; f < numFields; ++f) {
7841             PetscInt fDof = 0;
7842 
7843             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7844             newOffsets[f + 1] += fDof;
7845           }
7846         }
7847       } else {
7848         /* this point is not constrained */
7849         newNumPoints++;
7850         newNumIndices += bSecDof;
7851         for (PetscInt f = 0; f < numFields; ++f) {
7852           PetscInt fDof;
7853 
7854           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7855           newOffsets[f + 1] += fDof;
7856         }
7857       }
7858     }
7859   }
7860   if (!anyConstrained) {
7861     if (outNumPoints) *outNumPoints = 0;
7862     if (outNumIndices) *outNumIndices = 0;
7863     if (outPoints) *outPoints = NULL;
7864     if (outMat) *outMat = NULL;
7865     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7866     PetscFunctionReturn(PETSC_SUCCESS);
7867   }
7868 
7869   if (outNumPoints) *outNumPoints = newNumPoints;
7870   if (outNumIndices) *outNumIndices = newNumIndices;
7871 
7872   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7873   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7874 
7875   if (!outPoints && !outMat) {
7876     if (offsets) {
7877       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7878     }
7879     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7880     PetscFunctionReturn(PETSC_SUCCESS);
7881   }
7882 
7883   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7884   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7885 
7886   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7887   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7888 
7889   /* output arrays */
7890   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7891   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7892 
7893   // get the new Points
7894   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7895     PetscInt b    = points[2 * p];
7896     PetscInt bDof = 0, bSecDof = 0, bOff;
7897 
7898     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7899     if (!bSecDof) continue;
7900     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7901     if (bDof) {
7902       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7903       for (PetscInt q = 0; q < bDof; q++) {
7904         PetscInt a = anchors[bOff + q], aDof = 0;
7905 
7906         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7907         if (aDof) {
7908           newPoints[2 * newP]     = a;
7909           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7910           newP++;
7911         }
7912       }
7913     } else {
7914       newPoints[2 * newP]     = b;
7915       newPoints[2 * newP + 1] = points[2 * p + 1];
7916       newP++;
7917     }
7918   }
7919 
7920   if (outMat) {
7921     PetscScalar *tmpMat;
7922     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7923     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7924 
7925     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7926     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7927     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7928     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7929 
7930     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7931     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7932 
7933     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7934     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7935 
7936     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7937     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7938     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7939     // for each field, insert the anchor modification into modMat
7940     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7941       PetscInt fStart    = oldOffsets[f];
7942       PetscInt fNewStart = newOffsets[f];
7943       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7944         PetscInt b    = points[2 * p];
7945         PetscInt bDof = 0, bSecDof = 0, bOff;
7946 
7947         if (b >= sStart && b < sEnd) {
7948           if (numFields) {
7949             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7950           } else {
7951             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7952           }
7953         }
7954         if (!bSecDof) continue;
7955         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7956         if (bDof) {
7957           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7958           for (PetscInt q = 0; q < bDof; q++, newP++) {
7959             PetscInt a = anchors[bOff + q], aDof = 0;
7960 
7961             if (a >= sStart && a < sEnd) {
7962               if (numFields) {
7963                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7964               } else {
7965                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7966               }
7967             }
7968             if (aDof) {
7969               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7970               for (PetscInt d = 0; d < bSecDof; d++) {
7971                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7972               }
7973             }
7974             oNew += aDof;
7975           }
7976         } else {
7977           // Insert the identity matrix in this block
7978           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7979           oNew += bSecDof;
7980           newP++;
7981         }
7982         o += bSecDof;
7983       }
7984     }
7985 
7986     *outMat = modMat;
7987 
7988     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7989     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7990     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7991     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7992     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7993   }
7994   PetscCall(ISRestoreIndices(aIS, &anchors));
7995 
7996   /* output */
7997   if (outPoints) {
7998     *outPoints = newPoints;
7999   } else {
8000     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
8001   }
8002   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
8003   PetscFunctionReturn(PETSC_SUCCESS);
8004 }
8005 
8006 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)
8007 {
8008   PetscScalar *modMat        = NULL;
8009   PetscInt     newNumIndices = -1;
8010 
8011   PetscFunctionBegin;
8012   /* 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.
8013      modMat is that matrix C */
8014   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
8015   if (outNumIndices) *outNumIndices = newNumIndices;
8016   if (modMat) {
8017     const PetscScalar *newValues = values;
8018 
8019     if (multiplyRight) {
8020       PetscScalar *newNewValues = NULL;
8021       PetscBLASInt M, N, K;
8022       PetscScalar  a = 1.0, b = 0.0;
8023 
8024       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);
8025 
8026       PetscCall(PetscBLASIntCast(newNumIndices, &M));
8027       PetscCall(PetscBLASIntCast(numRows, &N));
8028       PetscCall(PetscBLASIntCast(numIndices, &K));
8029       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
8030       // row-major to column-major conversion, right multiplication becomes left multiplication
8031       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
8032       numCols   = newNumIndices;
8033       newValues = newNewValues;
8034     }
8035 
8036     if (multiplyLeft) {
8037       PetscScalar *newNewValues = NULL;
8038       PetscBLASInt M, N, K;
8039       PetscScalar  a = 1.0, b = 0.0;
8040 
8041       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);
8042 
8043       PetscCall(PetscBLASIntCast(numCols, &M));
8044       PetscCall(PetscBLASIntCast(newNumIndices, &N));
8045       PetscCall(PetscBLASIntCast(numIndices, &K));
8046       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
8047       // row-major to column-major conversion, left multiplication becomes right multiplication
8048       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
8049       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
8050       newValues = newNewValues;
8051     }
8052     *outValues = (PetscScalar *)newValues;
8053     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
8054   }
8055   PetscFunctionReturn(PETSC_SUCCESS);
8056 }
8057 
8058 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)
8059 {
8060   PetscFunctionBegin;
8061   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8062   PetscFunctionReturn(PETSC_SUCCESS);
8063 }
8064 
8065 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8066 {
8067   /* Closure ordering */
8068   PetscSection    clSection;
8069   IS              clPoints;
8070   const PetscInt *clp;
8071   PetscInt       *points;
8072   PetscInt        Ncl, Ni = 0;
8073 
8074   PetscFunctionBeginHot;
8075   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8076   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8077     PetscInt dof;
8078 
8079     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8080     Ni += dof;
8081   }
8082   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8083   *closureSize = Ni;
8084   PetscFunctionReturn(PETSC_SUCCESS);
8085 }
8086 
8087 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)
8088 {
8089   /* Closure ordering */
8090   PetscSection    clSection;
8091   IS              clPoints;
8092   const PetscInt *clp;
8093   PetscInt       *points;
8094   const PetscInt *clperm = NULL;
8095   /* Dof permutation and sign flips */
8096   const PetscInt    **perms[32] = {NULL};
8097   const PetscScalar **flips[32] = {NULL};
8098   PetscScalar        *valCopy   = NULL;
8099   /* Hanging node constraints */
8100   PetscInt    *pointsC = NULL;
8101   PetscScalar *valuesC = NULL;
8102   PetscInt     NclC, NiC;
8103 
8104   PetscInt *idx;
8105   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8106   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8107   PetscInt  idxStart, idxEnd;
8108   PetscInt  nRows, nCols;
8109 
8110   PetscFunctionBeginHot;
8111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8112   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8113   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8114   PetscAssertPointer(numRows, 6);
8115   PetscAssertPointer(numCols, 7);
8116   if (indices) PetscAssertPointer(indices, 8);
8117   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8118   if (values) PetscAssertPointer(values, 10);
8119   PetscCall(PetscSectionGetNumFields(section, &Nf));
8120   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8121   PetscCall(PetscArrayzero(offsets, 32));
8122   /* 1) Get points in closure */
8123   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8124   if (useClPerm) {
8125     PetscInt depth, clsize;
8126     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8127     for (clsize = 0, p = 0; p < Ncl; p++) {
8128       PetscInt dof;
8129       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8130       clsize += dof;
8131     }
8132     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8133   }
8134   /* 2) Get number of indices on these points and field offsets from section */
8135   for (p = 0; p < Ncl * 2; p += 2) {
8136     PetscInt dof, fdof;
8137 
8138     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8139     for (f = 0; f < Nf; ++f) {
8140       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8141       offsets[f + 1] += fdof;
8142     }
8143     Ni += dof;
8144   }
8145   if (*numRows == -1) *numRows = Ni;
8146   if (*numCols == -1) *numCols = Ni;
8147   nRows = *numRows;
8148   nCols = *numCols;
8149   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8150   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8151   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8152   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8153   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8154   for (f = 0; f < PetscMax(1, Nf); ++f) {
8155     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8156     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8157     /* may need to apply sign changes to the element matrix */
8158     if (values && flips[f]) {
8159       PetscInt foffset = offsets[f];
8160 
8161       for (p = 0; p < Ncl; ++p) {
8162         PetscInt           pnt  = points[2 * p], fdof;
8163         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8164 
8165         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8166         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8167         if (flip) {
8168           PetscInt i, j, k;
8169 
8170           if (!valCopy) {
8171             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8172             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8173             *values = valCopy;
8174           }
8175           for (i = 0; i < fdof; ++i) {
8176             PetscScalar fval = flip[i];
8177 
8178             if (multiplyRight) {
8179               for (k = 0; k < nRows; ++k) valCopy[Ni * k + (foffset + i)] *= fval;
8180             }
8181             if (multiplyLeft) {
8182               for (k = 0; k < nCols; ++k) valCopy[nCols * (foffset + i) + k] *= fval;
8183             }
8184           }
8185         }
8186         foffset += fdof;
8187       }
8188     }
8189   }
8190   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8191   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8192   if (NclC) {
8193     if (multiplyRight) *numCols = NiC;
8194     if (multiplyLeft) *numRows = NiC;
8195     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8196     for (f = 0; f < PetscMax(1, Nf); ++f) {
8197       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8198       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8199     }
8200     for (f = 0; f < PetscMax(1, Nf); ++f) {
8201       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8202       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8203     }
8204     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8205     Ncl    = NclC;
8206     Ni     = NiC;
8207     points = pointsC;
8208     if (values) *values = valuesC;
8209   }
8210   /* 5) Calculate indices */
8211   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8212   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8213   if (Nf) {
8214     PetscInt  idxOff;
8215     PetscBool useFieldOffsets;
8216 
8217     if (outOffsets) {
8218       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8219     }
8220     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8221     if (useFieldOffsets) {
8222       for (p = 0; p < Ncl; ++p) {
8223         const PetscInt pnt = points[p * 2];
8224 
8225         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8226       }
8227     } else {
8228       for (p = 0; p < Ncl; ++p) {
8229         const PetscInt pnt = points[p * 2];
8230 
8231         if (pnt < idxStart || pnt >= idxEnd) continue;
8232         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8233         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8234          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8235          * global section. */
8236         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8237       }
8238     }
8239   } else {
8240     PetscInt off = 0, idxOff;
8241 
8242     for (p = 0; p < Ncl; ++p) {
8243       const PetscInt  pnt  = points[p * 2];
8244       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8245 
8246       if (pnt < idxStart || pnt >= idxEnd) continue;
8247       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8248       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8249        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8250       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8251     }
8252   }
8253   /* 6) Cleanup */
8254   for (f = 0; f < PetscMax(1, Nf); ++f) {
8255     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8256     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8257   }
8258   if (NclC) {
8259     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8260   } else {
8261     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8262   }
8263 
8264   if (indices) *indices = idx;
8265   PetscFunctionReturn(PETSC_SUCCESS);
8266 }
8267 
8268 /*@C
8269   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8270 
8271   Not collective
8272 
8273   Input Parameters:
8274 + dm         - The `DM`
8275 . section    - The `PetscSection` describing the points (a local section)
8276 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8277 . point      - The point defining the closure
8278 - useClPerm  - Use the closure point permutation if available
8279 
8280   Output Parameters:
8281 + numIndices - The number of dof indices in the closure of point with the input sections
8282 . indices    - The dof indices
8283 . outOffsets - Array, of length the number of fields plus 1, to write the field offsets into, or `NULL`
8284 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8285 
8286   Level: advanced
8287 
8288   Notes:
8289   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8290 
8291   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8292   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8293   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8294   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8295   indices (with the above semantics) are implied.
8296 
8297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8298           `PetscSection`, `DMGetGlobalSection()`
8299 @*/
8300 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8301 {
8302   PetscInt numRows = -1, numCols = -1;
8303 
8304   PetscFunctionBeginHot;
8305   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8306   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8307   *numIndices = numRows;
8308   PetscFunctionReturn(PETSC_SUCCESS);
8309 }
8310 
8311 /*@C
8312   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8313 
8314   Not collective
8315 
8316   Input Parameters:
8317 + dm         - The `DM`
8318 . section    - The `PetscSection` describing the points (a local section)
8319 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8320 . point      - The point defining the closure
8321 - useClPerm  - Use the closure point permutation if available
8322 
8323   Output Parameters:
8324 + numIndices - The number of dof indices in the closure of point with the input sections
8325 . indices    - The dof indices
8326 . outOffsets - Array to write the field offsets into, or `NULL`
8327 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8328 
8329   Level: advanced
8330 
8331   Notes:
8332   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8333 
8334   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8335   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8336   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8337   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8338   indices (with the above semantics) are implied.
8339 
8340 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8341 @*/
8342 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8343 {
8344   PetscFunctionBegin;
8345   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8346   PetscAssertPointer(indices, 7);
8347   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8348   PetscFunctionReturn(PETSC_SUCCESS);
8349 }
8350 
8351 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8352 {
8353   DM_Plex           *mesh = (DM_Plex *)dm->data;
8354   PetscInt          *indices;
8355   PetscInt           numIndices;
8356   const PetscScalar *valuesOrig = values;
8357   PetscErrorCode     ierr;
8358 
8359   PetscFunctionBegin;
8360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8361   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8362   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8363   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8364   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8365   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8366 
8367   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8368 
8369   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8370   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8371   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8372   if (ierr) {
8373     PetscMPIInt rank;
8374 
8375     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8376     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8377     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8378     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8379     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8380     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8381   }
8382   if (mesh->printFEM > 1) {
8383     PetscInt i;
8384     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8385     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8386     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8387   }
8388 
8389   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8390   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8391   PetscFunctionReturn(PETSC_SUCCESS);
8392 }
8393 
8394 /*@C
8395   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8396 
8397   Not collective
8398 
8399   Input Parameters:
8400 + dm            - The `DM`
8401 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8402 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8403 . A             - The matrix
8404 . point         - The point in the `DM`
8405 . values        - The array of values
8406 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8407 
8408   Level: intermediate
8409 
8410 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8411 @*/
8412 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8413 {
8414   PetscFunctionBegin;
8415   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8416   PetscFunctionReturn(PETSC_SUCCESS);
8417 }
8418 
8419 /*@C
8420   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8421 
8422   Not collective
8423 
8424   Input Parameters:
8425 + dmRow            - The `DM` for the row fields
8426 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8427 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8428 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8429 . dmCol            - The `DM` for the column fields
8430 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8431 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8432 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8433 . A                - The matrix
8434 . point            - The point in the `DM`
8435 . values           - The array of values
8436 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8437 
8438   Level: intermediate
8439 
8440 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8441 @*/
8442 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)
8443 {
8444   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8445   PetscInt          *indicesRow, *indicesCol;
8446   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8447   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8448 
8449   PetscErrorCode ierr;
8450 
8451   PetscFunctionBegin;
8452   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8453   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8454   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8455   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8456   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8457   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8458   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8459   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8460   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8461   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8462   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8463 
8464   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8465   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8466   valuesV1 = valuesV0;
8467   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8468   valuesV2 = valuesV1;
8469   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8470 
8471   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8472   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8473   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8474   if (ierr) {
8475     PetscMPIInt rank;
8476 
8477     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8478     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8479     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8480     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8481     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8482     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8483     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8484   }
8485 
8486   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8487   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8488   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8489   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8490   PetscFunctionReturn(PETSC_SUCCESS);
8491 }
8492 
8493 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8494 {
8495   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8496   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8497   PetscInt       *cpoints = NULL;
8498   PetscInt       *findices, *cindices;
8499   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8500   PetscInt        foffsets[32], coffsets[32];
8501   DMPolytopeType  ct;
8502   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8503   PetscErrorCode  ierr;
8504 
8505   PetscFunctionBegin;
8506   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8507   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8508   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8509   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8510   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8511   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8512   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8513   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8514   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8515   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8516   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8517   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8518   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8519   PetscCall(PetscArrayzero(foffsets, 32));
8520   PetscCall(PetscArrayzero(coffsets, 32));
8521   /* Column indices */
8522   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8523   maxFPoints = numCPoints;
8524   /* Compress out points not in the section */
8525   /*   TODO: Squeeze out points with 0 dof as well */
8526   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8527   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8528     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8529       cpoints[q * 2]     = cpoints[p];
8530       cpoints[q * 2 + 1] = cpoints[p + 1];
8531       ++q;
8532     }
8533   }
8534   numCPoints = q;
8535   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8536     PetscInt fdof;
8537 
8538     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8539     if (!dof) continue;
8540     for (f = 0; f < numFields; ++f) {
8541       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8542       coffsets[f + 1] += fdof;
8543     }
8544     numCIndices += dof;
8545   }
8546   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8547   /* Row indices */
8548   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8549   {
8550     DMPlexTransform tr;
8551     DMPolytopeType *rct;
8552     PetscInt       *rsize, *rcone, *rornt, Nt;
8553 
8554     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8555     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8556     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8557     numSubcells = rsize[Nt - 1];
8558     PetscCall(DMPlexTransformDestroy(&tr));
8559   }
8560   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8561   for (r = 0, q = 0; r < numSubcells; ++r) {
8562     /* TODO Map from coarse to fine cells */
8563     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8564     /* Compress out points not in the section */
8565     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8566     for (p = 0; p < numFPoints * 2; p += 2) {
8567       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8568         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8569         if (!dof) continue;
8570         for (s = 0; s < q; ++s)
8571           if (fpoints[p] == ftotpoints[s * 2]) break;
8572         if (s < q) continue;
8573         ftotpoints[q * 2]     = fpoints[p];
8574         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8575         ++q;
8576       }
8577     }
8578     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8579   }
8580   numFPoints = q;
8581   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8582     PetscInt fdof;
8583 
8584     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8585     if (!dof) continue;
8586     for (f = 0; f < numFields; ++f) {
8587       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8588       foffsets[f + 1] += fdof;
8589     }
8590     numFIndices += dof;
8591   }
8592   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8593 
8594   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8595   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8596   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8597   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8598   if (numFields) {
8599     const PetscInt **permsF[32] = {NULL};
8600     const PetscInt **permsC[32] = {NULL};
8601 
8602     for (f = 0; f < numFields; f++) {
8603       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8604       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8605     }
8606     for (p = 0; p < numFPoints; p++) {
8607       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8608       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8609     }
8610     for (p = 0; p < numCPoints; p++) {
8611       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8612       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8613     }
8614     for (f = 0; f < numFields; f++) {
8615       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8616       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8617     }
8618   } else {
8619     const PetscInt **permsF = NULL;
8620     const PetscInt **permsC = NULL;
8621 
8622     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8623     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8624     for (p = 0, off = 0; p < numFPoints; p++) {
8625       const PetscInt *perm = permsF ? permsF[p] : NULL;
8626 
8627       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8628       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8629     }
8630     for (p = 0, off = 0; p < numCPoints; p++) {
8631       const PetscInt *perm = permsC ? permsC[p] : NULL;
8632 
8633       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8634       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8635     }
8636     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8637     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8638   }
8639   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8640   /* TODO: flips */
8641   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8642   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8643   if (ierr) {
8644     PetscMPIInt rank;
8645 
8646     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8647     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8648     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8649     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8650     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8651   }
8652   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8653   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8654   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8655   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8656   PetscFunctionReturn(PETSC_SUCCESS);
8657 }
8658 
8659 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8660 {
8661   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8662   PetscInt       *cpoints      = NULL;
8663   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8664   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8665   DMPolytopeType  ct;
8666   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8667 
8668   PetscFunctionBegin;
8669   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8670   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8671   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8672   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8673   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8674   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8675   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8676   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8677   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8678   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8679   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8680   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8681   /* Column indices */
8682   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8683   maxFPoints = numCPoints;
8684   /* Compress out points not in the section */
8685   /*   TODO: Squeeze out points with 0 dof as well */
8686   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8687   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8688     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8689       cpoints[q * 2]     = cpoints[p];
8690       cpoints[q * 2 + 1] = cpoints[p + 1];
8691       ++q;
8692     }
8693   }
8694   numCPoints = q;
8695   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8696     PetscInt fdof;
8697 
8698     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8699     if (!dof) continue;
8700     for (f = 0; f < numFields; ++f) {
8701       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8702       coffsets[f + 1] += fdof;
8703     }
8704     numCIndices += dof;
8705   }
8706   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8707   /* Row indices */
8708   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8709   {
8710     DMPlexTransform tr;
8711     DMPolytopeType *rct;
8712     PetscInt       *rsize, *rcone, *rornt, Nt;
8713 
8714     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8715     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8716     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8717     numSubcells = rsize[Nt - 1];
8718     PetscCall(DMPlexTransformDestroy(&tr));
8719   }
8720   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8721   for (r = 0, q = 0; r < numSubcells; ++r) {
8722     /* TODO Map from coarse to fine cells */
8723     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8724     /* Compress out points not in the section */
8725     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8726     for (p = 0; p < numFPoints * 2; p += 2) {
8727       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8728         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8729         if (!dof) continue;
8730         for (s = 0; s < q; ++s)
8731           if (fpoints[p] == ftotpoints[s * 2]) break;
8732         if (s < q) continue;
8733         ftotpoints[q * 2]     = fpoints[p];
8734         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8735         ++q;
8736       }
8737     }
8738     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8739   }
8740   numFPoints = q;
8741   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8742     PetscInt fdof;
8743 
8744     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8745     if (!dof) continue;
8746     for (f = 0; f < numFields; ++f) {
8747       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8748       foffsets[f + 1] += fdof;
8749     }
8750     numFIndices += dof;
8751   }
8752   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8753 
8754   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8755   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8756   if (numFields) {
8757     const PetscInt **permsF[32] = {NULL};
8758     const PetscInt **permsC[32] = {NULL};
8759 
8760     for (f = 0; f < numFields; f++) {
8761       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8762       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8763     }
8764     for (p = 0; p < numFPoints; p++) {
8765       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8766       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8767     }
8768     for (p = 0; p < numCPoints; p++) {
8769       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8770       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8771     }
8772     for (f = 0; f < numFields; f++) {
8773       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8774       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8775     }
8776   } else {
8777     const PetscInt **permsF = NULL;
8778     const PetscInt **permsC = NULL;
8779 
8780     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8781     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8782     for (p = 0, off = 0; p < numFPoints; p++) {
8783       const PetscInt *perm = permsF ? permsF[p] : NULL;
8784 
8785       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8786       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8787     }
8788     for (p = 0, off = 0; p < numCPoints; p++) {
8789       const PetscInt *perm = permsC ? permsC[p] : NULL;
8790 
8791       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8792       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8793     }
8794     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8795     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8796   }
8797   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8798   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8799   PetscFunctionReturn(PETSC_SUCCESS);
8800 }
8801 
8802 /*@
8803   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8804 
8805   Input Parameter:
8806 . dm - The `DMPLEX` object
8807 
8808   Output Parameter:
8809 . cellHeight - The height of a cell
8810 
8811   Level: developer
8812 
8813 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8814 @*/
8815 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8816 {
8817   DM_Plex *mesh = (DM_Plex *)dm->data;
8818 
8819   PetscFunctionBegin;
8820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8821   PetscAssertPointer(cellHeight, 2);
8822   *cellHeight = mesh->vtkCellHeight;
8823   PetscFunctionReturn(PETSC_SUCCESS);
8824 }
8825 
8826 /*@
8827   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8828 
8829   Input Parameters:
8830 + dm         - The `DMPLEX` object
8831 - cellHeight - The height of a cell
8832 
8833   Level: developer
8834 
8835 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8836 @*/
8837 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8838 {
8839   DM_Plex *mesh = (DM_Plex *)dm->data;
8840 
8841   PetscFunctionBegin;
8842   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8843   mesh->vtkCellHeight = cellHeight;
8844   PetscFunctionReturn(PETSC_SUCCESS);
8845 }
8846 
8847 /*@
8848   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8849 
8850   Input Parameters:
8851 + dm - The `DMPLEX` object
8852 - ct - The `DMPolytopeType` of the cell
8853 
8854   Output Parameters:
8855 + start - The first cell of this type, or `NULL`
8856 - end   - The upper bound on this celltype, or `NULL`
8857 
8858   Level: advanced
8859 
8860 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8861 @*/
8862 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8863 {
8864   DM_Plex *mesh = (DM_Plex *)dm->data;
8865   DMLabel  label;
8866   PetscInt pStart, pEnd;
8867 
8868   PetscFunctionBegin;
8869   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8870   if (start) {
8871     PetscAssertPointer(start, 3);
8872     *start = 0;
8873   }
8874   if (end) {
8875     PetscAssertPointer(end, 4);
8876     *end = 0;
8877   }
8878   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8879   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8880   if (mesh->tr) {
8881     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8882   } else {
8883     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8884     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8885     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8886   }
8887   PetscFunctionReturn(PETSC_SUCCESS);
8888 }
8889 
8890 /*@
8891   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8892 
8893   Input Parameters:
8894 + dm    - The `DMPLEX` object
8895 - depth - The depth for the given point stratum
8896 
8897   Output Parameter:
8898 . gsize - The global number of points in the stratum
8899 
8900   Level: advanced
8901 
8902 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8903 @*/
8904 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8905 {
8906   PetscSF         sf;
8907   const PetscInt *leaves;
8908   PetscInt        Nl, loc, start, end, lsize = 0;
8909 
8910   PetscFunctionBegin;
8911   PetscCall(DMGetPointSF(dm, &sf));
8912   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8913   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8914   for (PetscInt p = start; p < end; ++p) {
8915     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8916     if (loc < 0) ++lsize;
8917   }
8918   PetscCallMPI(MPIU_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8919   PetscFunctionReturn(PETSC_SUCCESS);
8920 }
8921 
8922 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8923 {
8924   PetscSection section, globalSection;
8925   PetscInt    *numbers, p;
8926 
8927   PetscFunctionBegin;
8928   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8929   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8930   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8931   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8932   PetscCall(PetscSectionSetUp(section));
8933   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8934   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8935   for (p = pStart; p < pEnd; ++p) {
8936     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8937     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8938     else numbers[p - pStart] += shift;
8939   }
8940   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8941   if (globalSize) {
8942     PetscLayout layout;
8943     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8944     PetscCall(PetscLayoutGetSize(layout, globalSize));
8945     PetscCall(PetscLayoutDestroy(&layout));
8946   }
8947   PetscCall(PetscSectionDestroy(&section));
8948   PetscCall(PetscSectionDestroy(&globalSection));
8949   PetscFunctionReturn(PETSC_SUCCESS);
8950 }
8951 
8952 /*@
8953   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8954 
8955   Input Parameters:
8956 + dm         - The `DMPLEX` object
8957 - includeAll - Whether to include all cells, or just the simplex and box cells
8958 
8959   Output Parameter:
8960 . globalCellNumbers - Global cell numbers for all cells on this process
8961 
8962   Level: developer
8963 
8964 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8965 @*/
8966 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8967 {
8968   PetscInt cellHeight, cStart, cEnd;
8969 
8970   PetscFunctionBegin;
8971   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8972   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8973   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8974   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8975   PetscFunctionReturn(PETSC_SUCCESS);
8976 }
8977 
8978 /*@
8979   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8980 
8981   Input Parameter:
8982 . dm - The `DMPLEX` object
8983 
8984   Output Parameter:
8985 . globalCellNumbers - Global cell numbers for all cells on this process
8986 
8987   Level: developer
8988 
8989 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8990 @*/
8991 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8992 {
8993   DM_Plex *mesh = (DM_Plex *)dm->data;
8994 
8995   PetscFunctionBegin;
8996   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8997   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8998   *globalCellNumbers = mesh->globalCellNumbers;
8999   PetscFunctionReturn(PETSC_SUCCESS);
9000 }
9001 
9002 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
9003 {
9004   PetscInt vStart, vEnd;
9005 
9006   PetscFunctionBegin;
9007   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9008   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9009   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
9010   PetscFunctionReturn(PETSC_SUCCESS);
9011 }
9012 
9013 /*@
9014   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
9015 
9016   Input Parameter:
9017 . dm - The `DMPLEX` object
9018 
9019   Output Parameter:
9020 . globalVertexNumbers - Global vertex numbers for all vertices on this process
9021 
9022   Level: developer
9023 
9024 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9025 @*/
9026 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
9027 {
9028   DM_Plex *mesh = (DM_Plex *)dm->data;
9029 
9030   PetscFunctionBegin;
9031   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9032   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
9033   *globalVertexNumbers = mesh->globalVertexNumbers;
9034   PetscFunctionReturn(PETSC_SUCCESS);
9035 }
9036 
9037 /*@
9038   DMPlexCreatePointNumbering - Create a global numbering for all points.
9039 
9040   Collective
9041 
9042   Input Parameter:
9043 . dm - The `DMPLEX` object
9044 
9045   Output Parameter:
9046 . globalPointNumbers - Global numbers for all points on this process
9047 
9048   Level: developer
9049 
9050   Notes:
9051   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
9052   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
9053   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
9054   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
9055 
9056   The partitioned mesh is
9057   ```
9058   (2)--0--(3)--1--(4)    (1)--0--(2)
9059   ```
9060   and its global numbering is
9061   ```
9062   (3)--0--(4)--1--(5)--2--(6)
9063   ```
9064   Then the global numbering is provided as
9065   ```
9066   [0] Number of indices in set 5
9067   [0] 0 0
9068   [0] 1 1
9069   [0] 2 3
9070   [0] 3 4
9071   [0] 4 -6
9072   [1] Number of indices in set 3
9073   [1] 0 2
9074   [1] 1 5
9075   [1] 2 6
9076   ```
9077 
9078 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9079 @*/
9080 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9081 {
9082   IS        nums[4];
9083   PetscInt  depths[4], gdepths[4], starts[4];
9084   PetscInt  depth, d, shift = 0;
9085   PetscBool empty = PETSC_FALSE;
9086 
9087   PetscFunctionBegin;
9088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9089   PetscCall(DMPlexGetDepth(dm, &depth));
9090   // For unstratified meshes use dim instead of depth
9091   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9092   // If any stratum is empty, we must mark all empty
9093   for (d = 0; d <= depth; ++d) {
9094     PetscInt end;
9095 
9096     depths[d] = depth - d;
9097     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9098     if (!(starts[d] - end)) empty = PETSC_TRUE;
9099   }
9100   if (empty)
9101     for (d = 0; d <= depth; ++d) {
9102       depths[d] = -1;
9103       starts[d] = -1;
9104     }
9105   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9106   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9107   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]);
9108   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9109   for (d = 0; d <= depth; ++d) {
9110     PetscInt pStart, pEnd, gsize;
9111 
9112     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9113     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9114     shift += gsize;
9115   }
9116   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9117   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9118   PetscFunctionReturn(PETSC_SUCCESS);
9119 }
9120 
9121 /*@
9122   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9123 
9124   Collective
9125 
9126   Input Parameter:
9127 . dm - The `DMPLEX` object
9128 
9129   Output Parameter:
9130 . globalEdgeNumbers - Global numbers for all edges on this process
9131 
9132   Level: developer
9133 
9134   Notes:
9135   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).
9136 
9137 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9138 @*/
9139 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9140 {
9141   PetscSF  sf;
9142   PetscInt eStart, eEnd;
9143 
9144   PetscFunctionBegin;
9145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9146   PetscCall(DMGetPointSF(dm, &sf));
9147   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9148   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9149   PetscFunctionReturn(PETSC_SUCCESS);
9150 }
9151 
9152 /*@
9153   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9154 
9155   Input Parameter:
9156 . dm - The `DMPLEX` object
9157 
9158   Output Parameter:
9159 . ranks - The rank field
9160 
9161   Options Database Key:
9162 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9163 
9164   Level: intermediate
9165 
9166 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9167 @*/
9168 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9169 {
9170   DM             rdm;
9171   PetscFE        fe;
9172   PetscScalar   *r;
9173   PetscMPIInt    rank;
9174   DMPolytopeType ct;
9175   PetscInt       dim, cStart, cEnd, c;
9176   PetscBool      simplex;
9177 
9178   PetscFunctionBeginUser;
9179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9180   PetscAssertPointer(ranks, 2);
9181   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9182   PetscCall(DMClone(dm, &rdm));
9183   PetscCall(DMGetDimension(rdm, &dim));
9184   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9185   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9186   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9187   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9188   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9189   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9190   PetscCall(PetscFEDestroy(&fe));
9191   PetscCall(DMCreateDS(rdm));
9192   PetscCall(DMCreateGlobalVector(rdm, ranks));
9193   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9194   PetscCall(VecGetArray(*ranks, &r));
9195   for (c = cStart; c < cEnd; ++c) {
9196     PetscScalar *lr;
9197 
9198     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9199     if (lr) *lr = rank;
9200   }
9201   PetscCall(VecRestoreArray(*ranks, &r));
9202   PetscCall(DMDestroy(&rdm));
9203   PetscFunctionReturn(PETSC_SUCCESS);
9204 }
9205 
9206 /*@
9207   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9208 
9209   Input Parameters:
9210 + dm    - The `DMPLEX`
9211 - label - The `DMLabel`
9212 
9213   Output Parameter:
9214 . val - The label value field
9215 
9216   Options Database Key:
9217 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9218 
9219   Level: intermediate
9220 
9221 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9222 @*/
9223 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9224 {
9225   DM             rdm, plex;
9226   Vec            lval;
9227   PetscSection   section;
9228   PetscFE        fe;
9229   PetscScalar   *v;
9230   PetscInt       dim, pStart, pEnd, p, cStart;
9231   DMPolytopeType ct;
9232   char           name[PETSC_MAX_PATH_LEN];
9233   const char    *lname, *prefix;
9234 
9235   PetscFunctionBeginUser;
9236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9237   PetscAssertPointer(label, 2);
9238   PetscAssertPointer(val, 3);
9239   PetscCall(DMClone(dm, &rdm));
9240   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9241   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9242   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9243   PetscCall(DMDestroy(&plex));
9244   PetscCall(DMGetDimension(rdm, &dim));
9245   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9246   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9247   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9248   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9249   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9250   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9251   PetscCall(PetscFEDestroy(&fe));
9252   PetscCall(DMCreateDS(rdm));
9253   PetscCall(DMCreateGlobalVector(rdm, val));
9254   PetscCall(DMCreateLocalVector(rdm, &lval));
9255   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9256   PetscCall(DMGetLocalSection(rdm, &section));
9257   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9258   PetscCall(VecGetArray(lval, &v));
9259   for (p = pStart; p < pEnd; ++p) {
9260     PetscInt cval, dof, off;
9261 
9262     PetscCall(PetscSectionGetDof(section, p, &dof));
9263     if (!dof) continue;
9264     PetscCall(DMLabelGetValue(label, p, &cval));
9265     PetscCall(PetscSectionGetOffset(section, p, &off));
9266     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9267   }
9268   PetscCall(VecRestoreArray(lval, &v));
9269   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9270   PetscCall(VecDestroy(&lval));
9271   PetscCall(DMDestroy(&rdm));
9272   PetscFunctionReturn(PETSC_SUCCESS);
9273 }
9274 
9275 /*@
9276   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9277 
9278   Input Parameter:
9279 . dm - The `DMPLEX` object
9280 
9281   Level: developer
9282 
9283   Notes:
9284   This is a useful diagnostic when creating meshes programmatically.
9285 
9286   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9287 
9288 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9289 @*/
9290 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9291 {
9292   PetscSection    coneSection, supportSection;
9293   const PetscInt *cone, *support;
9294   PetscInt        coneSize, c, supportSize, s;
9295   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9296   PetscBool       storagecheck = PETSC_TRUE;
9297 
9298   PetscFunctionBegin;
9299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9300   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9301   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9302   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9303   /* Check that point p is found in the support of its cone points, and vice versa */
9304   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9305   for (p = pStart; p < pEnd; ++p) {
9306     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9307     PetscCall(DMPlexGetCone(dm, p, &cone));
9308     for (c = 0; c < coneSize; ++c) {
9309       PetscBool dup = PETSC_FALSE;
9310       PetscInt  d;
9311       for (d = c - 1; d >= 0; --d) {
9312         if (cone[c] == cone[d]) {
9313           dup = PETSC_TRUE;
9314           break;
9315         }
9316       }
9317       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9318       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9319       for (s = 0; s < supportSize; ++s) {
9320         if (support[s] == p) break;
9321       }
9322       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9323         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9324         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9325         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9326         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9327         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9328         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9329         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]);
9330         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9331       }
9332     }
9333     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9334     if (p != pp) {
9335       storagecheck = PETSC_FALSE;
9336       continue;
9337     }
9338     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9339     PetscCall(DMPlexGetSupport(dm, p, &support));
9340     for (s = 0; s < supportSize; ++s) {
9341       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9342       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9343       for (c = 0; c < coneSize; ++c) {
9344         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9345         if (cone[c] != pp) {
9346           c = 0;
9347           break;
9348         }
9349         if (cone[c] == p) break;
9350       }
9351       if (c >= coneSize) {
9352         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9353         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9354         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9355         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9356         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9357         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9358         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9359       }
9360     }
9361   }
9362   if (storagecheck) {
9363     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9364     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9365     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9366   }
9367   PetscFunctionReturn(PETSC_SUCCESS);
9368 }
9369 
9370 /*
9371   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.
9372 */
9373 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9374 {
9375   DMPolytopeType  cct;
9376   PetscInt        ptpoints[4];
9377   const PetscInt *cone, *ccone, *ptcone;
9378   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9379 
9380   PetscFunctionBegin;
9381   *unsplit = 0;
9382   switch (ct) {
9383   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9384     ptpoints[npt++] = c;
9385     break;
9386   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9387     PetscCall(DMPlexGetCone(dm, c, &cone));
9388     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9389     for (cp = 0; cp < coneSize; ++cp) {
9390       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9391       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9392     }
9393     break;
9394   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9395   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9396     PetscCall(DMPlexGetCone(dm, c, &cone));
9397     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9398     for (cp = 0; cp < coneSize; ++cp) {
9399       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9400       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9401       for (ccp = 0; ccp < cconeSize; ++ccp) {
9402         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9403         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9404           PetscInt p;
9405           for (p = 0; p < npt; ++p)
9406             if (ptpoints[p] == ccone[ccp]) break;
9407           if (p == npt) ptpoints[npt++] = ccone[ccp];
9408         }
9409       }
9410     }
9411     break;
9412   default:
9413     break;
9414   }
9415   for (pt = 0; pt < npt; ++pt) {
9416     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9417     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9418   }
9419   PetscFunctionReturn(PETSC_SUCCESS);
9420 }
9421 
9422 /*@
9423   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9424 
9425   Input Parameters:
9426 + dm         - The `DMPLEX` object
9427 - cellHeight - Normally 0
9428 
9429   Level: developer
9430 
9431   Notes:
9432   This is a useful diagnostic when creating meshes programmatically.
9433   Currently applicable only to homogeneous simplex or tensor meshes.
9434 
9435   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9436 
9437 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9438 @*/
9439 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9440 {
9441   DMPlexInterpolatedFlag interp;
9442   DMPolytopeType         ct;
9443   PetscInt               vStart, vEnd, cStart, cEnd, c;
9444 
9445   PetscFunctionBegin;
9446   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9447   PetscCall(DMPlexIsInterpolated(dm, &interp));
9448   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9449   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9450   for (c = cStart; c < cEnd; ++c) {
9451     PetscInt *closure = NULL;
9452     PetscInt  coneSize, closureSize, cl, Nv = 0;
9453 
9454     PetscCall(DMPlexGetCellType(dm, c, &ct));
9455     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9456     if (interp == DMPLEX_INTERPOLATED_FULL) {
9457       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9458       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));
9459     }
9460     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9461     for (cl = 0; cl < closureSize * 2; cl += 2) {
9462       const PetscInt p = closure[cl];
9463       if ((p >= vStart) && (p < vEnd)) ++Nv;
9464     }
9465     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9466     /* Special Case: Tensor faces with identified vertices */
9467     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9468       PetscInt unsplit;
9469 
9470       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9471       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9472     }
9473     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));
9474   }
9475   PetscFunctionReturn(PETSC_SUCCESS);
9476 }
9477 
9478 /*@
9479   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9480 
9481   Collective
9482 
9483   Input Parameters:
9484 + dm         - The `DMPLEX` object
9485 - cellHeight - Normally 0
9486 
9487   Level: developer
9488 
9489   Notes:
9490   This is a useful diagnostic when creating meshes programmatically.
9491   This routine is only relevant for meshes that are fully interpolated across all ranks.
9492   It will error out if a partially interpolated mesh is given on some rank.
9493   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9494 
9495   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9496 
9497 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9498 @*/
9499 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9500 {
9501   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9502   DMPlexInterpolatedFlag interpEnum;
9503 
9504   PetscFunctionBegin;
9505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9506   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9507   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9508   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9509     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9510     PetscFunctionReturn(PETSC_SUCCESS);
9511   }
9512 
9513   PetscCall(DMGetDimension(dm, &dim));
9514   PetscCall(DMPlexGetDepth(dm, &depth));
9515   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9516   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9517     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9518     for (c = cStart; c < cEnd; ++c) {
9519       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9520       const DMPolytopeType *faceTypes;
9521       DMPolytopeType        ct;
9522       PetscInt              numFaces, coneSize, f;
9523       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9524 
9525       PetscCall(DMPlexGetCellType(dm, c, &ct));
9526       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9527       if (unsplit) continue;
9528       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9529       PetscCall(DMPlexGetCone(dm, c, &cone));
9530       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9531       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9532       for (cl = 0; cl < closureSize * 2; cl += 2) {
9533         const PetscInt p = closure[cl];
9534         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9535       }
9536       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9537       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);
9538       for (f = 0; f < numFaces; ++f) {
9539         DMPolytopeType fct;
9540         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9541 
9542         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9543         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9544         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9545           const PetscInt p = fclosure[cl];
9546           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9547         }
9548         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]);
9549         for (v = 0; v < fnumCorners; ++v) {
9550           if (fclosure[v] != faces[fOff + v]) {
9551             PetscInt v1;
9552 
9553             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9554             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9555             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9556             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9557             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9558             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]);
9559           }
9560         }
9561         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9562         fOff += faceSizes[f];
9563       }
9564       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9565       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9566     }
9567   }
9568   PetscFunctionReturn(PETSC_SUCCESS);
9569 }
9570 
9571 /*@
9572   DMPlexCheckGeometry - Check the geometry of mesh cells
9573 
9574   Input Parameter:
9575 . dm - The `DMPLEX` object
9576 
9577   Level: developer
9578 
9579   Notes:
9580   This is a useful diagnostic when creating meshes programmatically.
9581 
9582   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9583 
9584 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9585 @*/
9586 PetscErrorCode DMPlexCheckGeometry(DM dm)
9587 {
9588   Vec       coordinates;
9589   PetscReal detJ, J[9], refVol = 1.0;
9590   PetscReal vol;
9591   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9592 
9593   PetscFunctionBegin;
9594   PetscCall(DMGetDimension(dm, &dim));
9595   PetscCall(DMGetCoordinateDim(dm, &dE));
9596   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9597   PetscCall(DMPlexGetDepth(dm, &depth));
9598   for (d = 0; d < dim; ++d) refVol *= 2.0;
9599   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9600   /* Make sure local coordinates are created, because that step is collective */
9601   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9602   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9603   for (c = cStart; c < cEnd; ++c) {
9604     DMPolytopeType ct;
9605     PetscInt       unsplit;
9606     PetscBool      ignoreZeroVol = PETSC_FALSE;
9607 
9608     PetscCall(DMPlexGetCellType(dm, c, &ct));
9609     switch (ct) {
9610     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9611     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9612     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9613       ignoreZeroVol = PETSC_TRUE;
9614       break;
9615     default:
9616       break;
9617     }
9618     switch (ct) {
9619     case DM_POLYTOPE_TRI_PRISM:
9620     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9621     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9622     case DM_POLYTOPE_PYRAMID:
9623       continue;
9624     default:
9625       break;
9626     }
9627     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9628     if (unsplit) continue;
9629     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9630     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);
9631     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9632     /* This should work with periodicity since DG coordinates should be used */
9633     if (depth > 1) {
9634       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9635       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);
9636       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9637     }
9638   }
9639   PetscFunctionReturn(PETSC_SUCCESS);
9640 }
9641 
9642 /*@
9643   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9644 
9645   Collective
9646 
9647   Input Parameters:
9648 + dm              - The `DMPLEX` object
9649 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9650 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9651 
9652   Level: developer
9653 
9654   Notes:
9655   This is mainly intended for debugging/testing purposes.
9656 
9657   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9658 
9659   Extra roots can come from periodic cuts, where additional points appear on the boundary
9660 
9661 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9662 @*/
9663 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9664 {
9665   PetscInt           l, nleaves, nroots, overlap;
9666   const PetscInt    *locals;
9667   const PetscSFNode *remotes;
9668   PetscBool          distributed;
9669   MPI_Comm           comm;
9670   PetscMPIInt        rank;
9671 
9672   PetscFunctionBegin;
9673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9674   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9675   else pointSF = dm->sf;
9676   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9677   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9678   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9679   {
9680     PetscMPIInt mpiFlag;
9681 
9682     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9683     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9684   }
9685   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9686   PetscCall(DMPlexIsDistributed(dm, &distributed));
9687   if (!distributed) {
9688     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);
9689     PetscFunctionReturn(PETSC_SUCCESS);
9690   }
9691   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);
9692   PetscCall(DMPlexGetOverlap(dm, &overlap));
9693 
9694   /* Check SF graph is compatible with DMPlex chart */
9695   {
9696     PetscInt pStart, pEnd, maxLeaf;
9697 
9698     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9699     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9700     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9701     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9702   }
9703 
9704   /* Check there are no cells in interface */
9705   if (!overlap) {
9706     PetscInt cellHeight, cStart, cEnd;
9707 
9708     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9709     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9710     for (l = 0; l < nleaves; ++l) {
9711       const PetscInt point = locals ? locals[l] : l;
9712 
9713       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9714     }
9715   }
9716 
9717   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9718   {
9719     const PetscInt *rootdegree;
9720 
9721     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9722     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9723     for (l = 0; l < nleaves; ++l) {
9724       const PetscInt  point = locals ? locals[l] : l;
9725       const PetscInt *cone;
9726       PetscInt        coneSize, c, idx;
9727 
9728       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9729       PetscCall(DMPlexGetCone(dm, point, &cone));
9730       for (c = 0; c < coneSize; ++c) {
9731         if (!rootdegree[cone[c]]) {
9732           if (locals) {
9733             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9734           } else {
9735             idx = (cone[c] < nleaves) ? cone[c] : -1;
9736           }
9737           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9738         }
9739       }
9740     }
9741   }
9742   PetscFunctionReturn(PETSC_SUCCESS);
9743 }
9744 
9745 /*@
9746   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9747 
9748   Collective
9749 
9750   Input Parameter:
9751 . dm - The `DMPLEX` object
9752 
9753   Level: developer
9754 
9755   Notes:
9756   This is mainly intended for debugging/testing purposes.
9757 
9758   Other cell types which are disconnected would be caught by the symmetry and face checks.
9759 
9760   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9761 
9762 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9763 @*/
9764 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9765 {
9766   PetscInt pStart, pEnd, vStart, vEnd;
9767 
9768   PetscFunctionBegin;
9769   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9770   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9771   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9772   for (PetscInt v = vStart; v < vEnd; ++v) {
9773     PetscInt suppSize;
9774 
9775     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9776     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9777   }
9778   PetscFunctionReturn(PETSC_SUCCESS);
9779 }
9780 
9781 /*@
9782   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9783 
9784   Input Parameter:
9785 . dm - The `DMPLEX` object
9786 
9787   Level: developer
9788 
9789   Notes:
9790   This is a useful diagnostic when creating meshes programmatically.
9791 
9792   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9793 
9794   Currently does not include `DMPlexCheckCellShape()`.
9795 
9796 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9797 @*/
9798 PetscErrorCode DMPlexCheck(DM dm)
9799 {
9800   PetscInt cellHeight;
9801 
9802   PetscFunctionBegin;
9803   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9804   PetscCall(DMPlexCheckSymmetry(dm));
9805   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9806   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9807   PetscCall(DMPlexCheckGeometry(dm));
9808   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9809   PetscCall(DMPlexCheckInterfaceCones(dm));
9810   PetscCall(DMPlexCheckOrphanVertices(dm));
9811   PetscFunctionReturn(PETSC_SUCCESS);
9812 }
9813 
9814 typedef struct cell_stats {
9815   PetscReal min, max, sum, squaresum;
9816   PetscInt  count;
9817 } cell_stats_t;
9818 
9819 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9820 {
9821   PetscInt i, N = *len;
9822 
9823   for (i = 0; i < N; i++) {
9824     cell_stats_t *A = (cell_stats_t *)a;
9825     cell_stats_t *B = (cell_stats_t *)b;
9826 
9827     B->min = PetscMin(A->min, B->min);
9828     B->max = PetscMax(A->max, B->max);
9829     B->sum += A->sum;
9830     B->squaresum += A->squaresum;
9831     B->count += A->count;
9832   }
9833 }
9834 
9835 /*@
9836   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9837 
9838   Collective
9839 
9840   Input Parameters:
9841 + dm        - The `DMPLEX` object
9842 . output    - If true, statistics will be displayed on `stdout`
9843 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9844 
9845   Level: developer
9846 
9847   Notes:
9848   This is mainly intended for debugging/testing purposes.
9849 
9850   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9851 
9852 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9853 @*/
9854 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9855 {
9856   DM           dmCoarse;
9857   cell_stats_t stats, globalStats;
9858   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9859   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9860   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9861   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9862   PetscMPIInt  rank, size;
9863 
9864   PetscFunctionBegin;
9865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9866   stats.min = PETSC_MAX_REAL;
9867   stats.max = PETSC_MIN_REAL;
9868   stats.sum = stats.squaresum = 0.;
9869   stats.count                 = 0;
9870 
9871   PetscCallMPI(MPI_Comm_size(comm, &size));
9872   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9873   PetscCall(DMGetCoordinateDim(dm, &cdim));
9874   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9875   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9876   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9877   for (c = cStart; c < cEnd; c++) {
9878     PetscInt  i;
9879     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9880 
9881     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9882     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9883     for (i = 0; i < PetscSqr(cdim); ++i) {
9884       frobJ += J[i] * J[i];
9885       frobInvJ += invJ[i] * invJ[i];
9886     }
9887     cond2 = frobJ * frobInvJ;
9888     cond  = PetscSqrtReal(cond2);
9889 
9890     stats.min = PetscMin(stats.min, cond);
9891     stats.max = PetscMax(stats.max, cond);
9892     stats.sum += cond;
9893     stats.squaresum += cond2;
9894     stats.count++;
9895     if (output && cond > limit) {
9896       PetscSection coordSection;
9897       Vec          coordsLocal;
9898       PetscScalar *coords = NULL;
9899       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9900 
9901       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9902       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9903       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9904       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9905       for (i = 0; i < Nv / cdim; ++i) {
9906         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9907         for (d = 0; d < cdim; ++d) {
9908           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9909           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9910         }
9911         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9912       }
9913       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9914       for (cl = 0; cl < clSize * 2; cl += 2) {
9915         const PetscInt edge = closure[cl];
9916 
9917         if ((edge >= eStart) && (edge < eEnd)) {
9918           PetscReal len;
9919 
9920           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9921           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9922         }
9923       }
9924       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9925       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9926     }
9927   }
9928   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9929 
9930   if (size > 1) {
9931     PetscMPIInt  blockLengths[2] = {4, 1};
9932     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9933     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9934     MPI_Op       statReduce;
9935 
9936     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9937     PetscCallMPI(MPI_Type_commit(&statType));
9938     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9939     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9940     PetscCallMPI(MPI_Op_free(&statReduce));
9941     PetscCallMPI(MPI_Type_free(&statType));
9942   } else {
9943     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9944   }
9945   if (rank == 0) {
9946     count = globalStats.count;
9947     min   = globalStats.min;
9948     max   = globalStats.max;
9949     mean  = globalStats.sum / globalStats.count;
9950     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9951   }
9952 
9953   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));
9954   PetscCall(PetscFree2(J, invJ));
9955 
9956   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9957   if (dmCoarse) {
9958     PetscBool isplex;
9959 
9960     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9961     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9962   }
9963   PetscFunctionReturn(PETSC_SUCCESS);
9964 }
9965 
9966 /*@
9967   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9968   orthogonal quality below given tolerance.
9969 
9970   Collective
9971 
9972   Input Parameters:
9973 + dm   - The `DMPLEX` object
9974 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9975 - atol - [0, 1] Absolute tolerance for tagging cells.
9976 
9977   Output Parameters:
9978 + OrthQual      - `Vec` containing orthogonal quality per cell
9979 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9980 
9981   Options Database Keys:
9982 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9983 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9984 
9985   Level: intermediate
9986 
9987   Notes:
9988   Orthogonal quality is given by the following formula\:
9989 
9990   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9991 
9992   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
9993   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9994   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9995   calculating the cosine of the angle between these vectors.
9996 
9997   Orthogonal quality ranges from 1 (best) to 0 (worst).
9998 
9999   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
10000   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
10001 
10002   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
10003 
10004 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
10005 @*/
10006 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
10007 {
10008   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
10009   PetscInt              *idx;
10010   PetscScalar           *oqVals;
10011   const PetscScalar     *cellGeomArr, *faceGeomArr;
10012   PetscReal             *ci, *fi, *Ai;
10013   MPI_Comm               comm;
10014   Vec                    cellgeom, facegeom;
10015   DM                     dmFace, dmCell;
10016   IS                     glob;
10017   ISLocalToGlobalMapping ltog;
10018   PetscViewer            vwr;
10019 
10020   PetscFunctionBegin;
10021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10022   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
10023   PetscAssertPointer(OrthQual, 4);
10024   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
10025   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
10026   PetscCall(DMGetDimension(dm, &nc));
10027   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
10028   {
10029     DMPlexInterpolatedFlag interpFlag;
10030 
10031     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
10032     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
10033       PetscMPIInt rank;
10034 
10035       PetscCallMPI(MPI_Comm_rank(comm, &rank));
10036       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
10037     }
10038   }
10039   if (OrthQualLabel) {
10040     PetscAssertPointer(OrthQualLabel, 5);
10041     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
10042     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
10043   } else {
10044     *OrthQualLabel = NULL;
10045   }
10046   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
10047   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
10048   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
10049   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
10050   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
10051   PetscCall(VecCreate(comm, OrthQual));
10052   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
10053   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
10054   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
10055   PetscCall(VecSetUp(*OrthQual));
10056   PetscCall(ISDestroy(&glob));
10057   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
10058   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
10059   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
10060   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10061   PetscCall(VecGetDM(cellgeom, &dmCell));
10062   PetscCall(VecGetDM(facegeom, &dmFace));
10063   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10064   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10065     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10066     PetscInt         cellarr[2], *adj = NULL;
10067     PetscScalar     *cArr, *fArr;
10068     PetscReal        minvalc = 1.0, minvalf = 1.0;
10069     PetscFVCellGeom *cg;
10070 
10071     idx[cellIter] = cell - cStart;
10072     cellarr[0]    = cell;
10073     /* Make indexing into cellGeom easier */
10074     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10075     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10076     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10077     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10078     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10079       PetscInt         i;
10080       const PetscInt   neigh  = adj[cellneigh];
10081       PetscReal        normci = 0, normfi = 0, normai = 0;
10082       PetscFVCellGeom *cgneigh;
10083       PetscFVFaceGeom *fg;
10084 
10085       /* Don't count ourselves in the neighbor list */
10086       if (neigh == cell) continue;
10087       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10088       cellarr[1] = neigh;
10089       {
10090         PetscInt        numcovpts;
10091         const PetscInt *covpts;
10092 
10093         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10094         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10095         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10096       }
10097 
10098       /* Compute c_i, f_i and their norms */
10099       for (i = 0; i < nc; i++) {
10100         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10101         fi[i] = fg->centroid[i] - cg->centroid[i];
10102         Ai[i] = fg->normal[i];
10103         normci += PetscPowReal(ci[i], 2);
10104         normfi += PetscPowReal(fi[i], 2);
10105         normai += PetscPowReal(Ai[i], 2);
10106       }
10107       normci = PetscSqrtReal(normci);
10108       normfi = PetscSqrtReal(normfi);
10109       normai = PetscSqrtReal(normai);
10110 
10111       /* Normalize and compute for each face-cell-normal pair */
10112       for (i = 0; i < nc; i++) {
10113         ci[i] = ci[i] / normci;
10114         fi[i] = fi[i] / normfi;
10115         Ai[i] = Ai[i] / normai;
10116         /* PetscAbs because I don't know if normals are guaranteed to point out */
10117         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10118         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10119       }
10120       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10121       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10122     }
10123     PetscCall(PetscFree(adj));
10124     PetscCall(PetscFree2(cArr, fArr));
10125     /* Defer to cell if they're equal */
10126     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10127     if (OrthQualLabel) {
10128       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10129     }
10130   }
10131   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10132   PetscCall(VecAssemblyBegin(*OrthQual));
10133   PetscCall(VecAssemblyEnd(*OrthQual));
10134   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10135   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10136   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10137   if (OrthQualLabel) {
10138     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10139   }
10140   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10141   PetscCall(PetscViewerDestroy(&vwr));
10142   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10143   PetscFunctionReturn(PETSC_SUCCESS);
10144 }
10145 
10146 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10147  * interpolator construction */
10148 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10149 {
10150   PetscSection section, newSection, gsection;
10151   PetscSF      sf;
10152   PetscBool    hasConstraints, ghasConstraints;
10153 
10154   PetscFunctionBegin;
10155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10156   PetscAssertPointer(odm, 2);
10157   PetscCall(DMGetLocalSection(dm, &section));
10158   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10159   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10160   if (!ghasConstraints) {
10161     PetscCall(PetscObjectReference((PetscObject)dm));
10162     *odm = dm;
10163     PetscFunctionReturn(PETSC_SUCCESS);
10164   }
10165   PetscCall(DMClone(dm, odm));
10166   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10167   PetscCall(DMGetLocalSection(*odm, &newSection));
10168   PetscCall(DMGetPointSF(*odm, &sf));
10169   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10170   PetscCall(DMSetGlobalSection(*odm, gsection));
10171   PetscCall(PetscSectionDestroy(&gsection));
10172   PetscFunctionReturn(PETSC_SUCCESS);
10173 }
10174 
10175 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10176 {
10177   DM        dmco, dmfo;
10178   Mat       interpo;
10179   Vec       rscale;
10180   Vec       cglobalo, clocal;
10181   Vec       fglobal, fglobalo, flocal;
10182   PetscBool regular;
10183 
10184   PetscFunctionBegin;
10185   PetscCall(DMGetFullDM(dmc, &dmco));
10186   PetscCall(DMGetFullDM(dmf, &dmfo));
10187   PetscCall(DMSetCoarseDM(dmfo, dmco));
10188   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10189   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10190   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10191   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10192   PetscCall(DMCreateLocalVector(dmc, &clocal));
10193   PetscCall(VecSet(cglobalo, 0.));
10194   PetscCall(VecSet(clocal, 0.));
10195   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10196   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10197   PetscCall(DMCreateLocalVector(dmf, &flocal));
10198   PetscCall(VecSet(fglobal, 0.));
10199   PetscCall(VecSet(fglobalo, 0.));
10200   PetscCall(VecSet(flocal, 0.));
10201   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10202   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10203   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10204   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10205   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10206   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10207   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10208   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10209   *shift = fglobal;
10210   PetscCall(VecDestroy(&flocal));
10211   PetscCall(VecDestroy(&fglobalo));
10212   PetscCall(VecDestroy(&clocal));
10213   PetscCall(VecDestroy(&cglobalo));
10214   PetscCall(VecDestroy(&rscale));
10215   PetscCall(MatDestroy(&interpo));
10216   PetscCall(DMDestroy(&dmfo));
10217   PetscCall(DMDestroy(&dmco));
10218   PetscFunctionReturn(PETSC_SUCCESS);
10219 }
10220 
10221 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10222 {
10223   PetscObject shifto;
10224   Vec         shift;
10225 
10226   PetscFunctionBegin;
10227   if (!interp) {
10228     Vec rscale;
10229 
10230     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10231     PetscCall(VecDestroy(&rscale));
10232   } else {
10233     PetscCall(PetscObjectReference((PetscObject)interp));
10234   }
10235   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10236   if (!shifto) {
10237     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10238     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10239     shifto = (PetscObject)shift;
10240     PetscCall(VecDestroy(&shift));
10241   }
10242   shift = (Vec)shifto;
10243   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10244   PetscCall(VecAXPY(fineSol, 1.0, shift));
10245   PetscCall(MatDestroy(&interp));
10246   PetscFunctionReturn(PETSC_SUCCESS);
10247 }
10248 
10249 /* Pointwise interpolation
10250      Just code FEM for now
10251      u^f = I u^c
10252      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10253      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10254      I_{ij} = psi^f_i phi^c_j
10255 */
10256 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10257 {
10258   PetscSection gsc, gsf;
10259   PetscInt     m, n;
10260   void        *ctx;
10261   DM           cdm;
10262   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10263 
10264   PetscFunctionBegin;
10265   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10266   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10267   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10268   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10269 
10270   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10271   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10272   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10273   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10274   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10275 
10276   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10277   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10278   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10279   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10280   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10281   if (scaling) {
10282     /* Use naive scaling */
10283     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10284   }
10285   PetscFunctionReturn(PETSC_SUCCESS);
10286 }
10287 
10288 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10289 {
10290   VecScatter ctx;
10291 
10292   PetscFunctionBegin;
10293   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10294   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10295   PetscCall(VecScatterDestroy(&ctx));
10296   PetscFunctionReturn(PETSC_SUCCESS);
10297 }
10298 
10299 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[])
10300 {
10301   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10302   const PetscInt Nc = uOff[f + 1] - uOff[f];
10303   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10304 }
10305 
10306 // The assumption here is that the test field is a vector and the basis field is a scalar (so we need the gradient)
10307 static void g1_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 g1[])
10308 {
10309   for (PetscInt c = 0; c < dim; ++c) g1[c * dim + c] = 1.0;
10310 }
10311 
10312 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10313 {
10314   DM           dmc;
10315   PetscDS      ds;
10316   Vec          ones, locmass;
10317   IS           cellIS;
10318   PetscFormKey key;
10319   PetscInt     depth;
10320 
10321   PetscFunctionBegin;
10322   PetscCall(DMClone(dm, &dmc));
10323   PetscCall(DMCopyDisc(dm, dmc));
10324   PetscCall(DMGetDS(dmc, &ds));
10325   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10326   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10327   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10328   else PetscCall(DMGetLocalVector(dm, &locmass));
10329   PetscCall(DMGetLocalVector(dm, &ones));
10330   PetscCall(DMPlexGetDepth(dm, &depth));
10331   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10332   PetscCall(VecSet(locmass, 0.0));
10333   PetscCall(VecSet(ones, 1.0));
10334   key.label = NULL;
10335   key.value = 0;
10336   key.field = 0;
10337   key.part  = 0;
10338   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10339   PetscCall(ISDestroy(&cellIS));
10340   if (mass) {
10341     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10342     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10343   }
10344   PetscCall(DMRestoreLocalVector(dm, &ones));
10345   if (lmass) *lmass = locmass;
10346   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10347   PetscCall(DMDestroy(&dmc));
10348   PetscFunctionReturn(PETSC_SUCCESS);
10349 }
10350 
10351 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10352 {
10353   PetscSection gsc, gsf;
10354   PetscInt     m, n;
10355   void        *ctx;
10356   DM           cdm;
10357   PetscBool    regular;
10358 
10359   PetscFunctionBegin;
10360   if (dmFine == dmCoarse) {
10361     DM            dmc;
10362     PetscDS       ds;
10363     PetscWeakForm wf;
10364     Vec           u;
10365     IS            cellIS;
10366     PetscFormKey  key;
10367     PetscInt      depth;
10368 
10369     PetscCall(DMClone(dmFine, &dmc));
10370     PetscCall(DMCopyDisc(dmFine, dmc));
10371     PetscCall(DMGetDS(dmc, &ds));
10372     PetscCall(PetscDSGetWeakForm(ds, &wf));
10373     PetscCall(PetscWeakFormClear(wf));
10374     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10375     PetscCall(DMCreateMatrix(dmc, mass));
10376     PetscCall(DMGetLocalVector(dmc, &u));
10377     PetscCall(DMPlexGetDepth(dmc, &depth));
10378     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10379     PetscCall(MatZeroEntries(*mass));
10380     key.label = NULL;
10381     key.value = 0;
10382     key.field = 0;
10383     key.part  = 0;
10384     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10385     PetscCall(ISDestroy(&cellIS));
10386     PetscCall(DMRestoreLocalVector(dmc, &u));
10387     PetscCall(DMDestroy(&dmc));
10388   } else {
10389     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10390     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10391     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10392     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10393 
10394     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10395     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10396     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10397     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10398 
10399     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10400     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10401     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10402     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10403   }
10404   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10405   PetscFunctionReturn(PETSC_SUCCESS);
10406 }
10407 
10408 PetscErrorCode DMCreateGradientMatrix_Plex(DM dmc, DM dmr, Mat *derv)
10409 {
10410   PetscSection gsc, gsf;
10411   PetscInt     m, n;
10412   void        *ctx;
10413 
10414   PetscFunctionBegin;
10415   PetscCall(DMGetGlobalSection(dmr, &gsf));
10416   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10417   PetscCall(DMGetGlobalSection(dmc, &gsc));
10418   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10419 
10420   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmc), derv));
10421   PetscCall(PetscObjectSetName((PetscObject)*derv, "Plex Derivative Matrix"));
10422   PetscCall(MatSetSizes(*derv, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10423   PetscCall(MatSetType(*derv, dmc->mattype));
10424 
10425   PetscCall(DMGetApplicationContext(dmr, &ctx));
10426   {
10427     DM            ndmr;
10428     PetscDS       ds;
10429     PetscWeakForm wf;
10430     Vec           u;
10431     IS            cellIS;
10432     PetscFormKey  key;
10433     PetscInt      depth, Nf;
10434 
10435     PetscCall(DMClone(dmr, &ndmr));
10436     PetscCall(DMCopyDisc(dmr, ndmr));
10437     PetscCall(DMGetDS(ndmr, &ds));
10438     PetscCall(PetscDSGetWeakForm(ds, &wf));
10439     PetscCall(PetscWeakFormClear(wf));
10440     PetscCall(PetscDSGetNumFields(ds, &Nf));
10441     for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, NULL, g1_identity_private, NULL, NULL));
10442     PetscCall(DMGetLocalVector(ndmr, &u));
10443     PetscCall(DMPlexGetDepth(ndmr, &depth));
10444     PetscCall(DMGetStratumIS(ndmr, "depth", depth, &cellIS));
10445     PetscCall(MatZeroEntries(*derv));
10446     key.label = NULL;
10447     key.value = 0;
10448     key.field = 0;
10449     key.part  = 0;
10450     PetscCall(DMPlexComputeJacobianByKeyGeneral(ndmr, dmc, key, cellIS, 0.0, 0.0, u, NULL, *derv, *derv, NULL));
10451     PetscCall(ISDestroy(&cellIS));
10452     PetscCall(DMRestoreLocalVector(ndmr, &u));
10453     PetscCall(DMDestroy(&ndmr));
10454   }
10455   PetscCall(MatViewFromOptions(*derv, NULL, "-gradient_mat_view"));
10456   PetscFunctionReturn(PETSC_SUCCESS);
10457 }
10458 
10459 /*@
10460   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10461 
10462   Input Parameter:
10463 . dm - The `DMPLEX` object
10464 
10465   Output Parameter:
10466 . regular - The flag
10467 
10468   Level: intermediate
10469 
10470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10471 @*/
10472 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10473 {
10474   PetscFunctionBegin;
10475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10476   PetscAssertPointer(regular, 2);
10477   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10478   PetscFunctionReturn(PETSC_SUCCESS);
10479 }
10480 
10481 /*@
10482   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10483 
10484   Input Parameters:
10485 + dm      - The `DMPLEX` object
10486 - regular - The flag
10487 
10488   Level: intermediate
10489 
10490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10491 @*/
10492 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10493 {
10494   PetscFunctionBegin;
10495   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10496   ((DM_Plex *)dm->data)->regularRefinement = regular;
10497   PetscFunctionReturn(PETSC_SUCCESS);
10498 }
10499 
10500 /*@
10501   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10502   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10503 
10504   Not Collective
10505 
10506   Input Parameter:
10507 . dm - The `DMPLEX` object
10508 
10509   Output Parameters:
10510 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10511 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10512 
10513   Level: intermediate
10514 
10515 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10516 @*/
10517 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10518 {
10519   DM_Plex *plex = (DM_Plex *)dm->data;
10520 
10521   PetscFunctionBegin;
10522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10523   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10524   if (anchorSection) *anchorSection = plex->anchorSection;
10525   if (anchorIS) *anchorIS = plex->anchorIS;
10526   PetscFunctionReturn(PETSC_SUCCESS);
10527 }
10528 
10529 /*@
10530   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10531 
10532   Collective
10533 
10534   Input Parameters:
10535 + dm            - The `DMPLEX` object
10536 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10537                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10538 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10539 
10540   Level: intermediate
10541 
10542   Notes:
10543   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10544   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10545   combination of other points' degrees of freedom.
10546 
10547   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10548   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10549 
10550   The reference counts of `anchorSection` and `anchorIS` are incremented.
10551 
10552 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10553 @*/
10554 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10555 {
10556   DM_Plex    *plex = (DM_Plex *)dm->data;
10557   PetscMPIInt result;
10558 
10559   PetscFunctionBegin;
10560   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10561   if (anchorSection) {
10562     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10563     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10564     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10565   }
10566   if (anchorIS) {
10567     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10568     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10569     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10570   }
10571 
10572   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10573   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10574   plex->anchorSection = anchorSection;
10575 
10576   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10577   PetscCall(ISDestroy(&plex->anchorIS));
10578   plex->anchorIS = anchorIS;
10579 
10580   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10581     PetscInt        size, a, pStart, pEnd;
10582     const PetscInt *anchors;
10583 
10584     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10585     PetscCall(ISGetLocalSize(anchorIS, &size));
10586     PetscCall(ISGetIndices(anchorIS, &anchors));
10587     for (a = 0; a < size; a++) {
10588       PetscInt p;
10589 
10590       p = anchors[a];
10591       if (p >= pStart && p < pEnd) {
10592         PetscInt dof;
10593 
10594         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10595         if (dof) {
10596           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10597           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10598         }
10599       }
10600     }
10601     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10602   }
10603   /* reset the generic constraints */
10604   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10605   PetscFunctionReturn(PETSC_SUCCESS);
10606 }
10607 
10608 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10609 {
10610   PetscSection anchorSection;
10611   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10612 
10613   PetscFunctionBegin;
10614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10615   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10616   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10617   PetscCall(PetscSectionGetNumFields(section, &numFields));
10618   if (numFields) {
10619     PetscInt f;
10620     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10621 
10622     for (f = 0; f < numFields; f++) {
10623       PetscInt numComp;
10624 
10625       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10626       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10627     }
10628   }
10629   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10630   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10631   pStart = PetscMax(pStart, sStart);
10632   pEnd   = PetscMin(pEnd, sEnd);
10633   pEnd   = PetscMax(pStart, pEnd);
10634   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10635   for (p = pStart; p < pEnd; p++) {
10636     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10637     if (dof) {
10638       PetscCall(PetscSectionGetDof(section, p, &dof));
10639       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10640       for (f = 0; f < numFields; f++) {
10641         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10642         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10643       }
10644     }
10645   }
10646   PetscCall(PetscSectionSetUp(*cSec));
10647   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10648   PetscFunctionReturn(PETSC_SUCCESS);
10649 }
10650 
10651 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10652 {
10653   PetscSection    aSec;
10654   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10655   const PetscInt *anchors;
10656   PetscInt        numFields, f;
10657   IS              aIS;
10658   MatType         mtype;
10659   PetscBool       iscuda, iskokkos;
10660 
10661   PetscFunctionBegin;
10662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10663   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10664   PetscCall(PetscSectionGetStorageSize(section, &n));
10665   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10666   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10667   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10668   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10669   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10670   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10671   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10672   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10673   else mtype = MATSEQAIJ;
10674   PetscCall(MatSetType(*cMat, mtype));
10675   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10676   PetscCall(ISGetIndices(aIS, &anchors));
10677   /* cSec will be a subset of aSec and section */
10678   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10679   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10680   PetscCall(PetscMalloc1(m + 1, &i));
10681   i[0] = 0;
10682   PetscCall(PetscSectionGetNumFields(section, &numFields));
10683   for (p = pStart; p < pEnd; p++) {
10684     PetscInt rDof, rOff, r;
10685 
10686     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10687     if (!rDof) continue;
10688     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10689     if (numFields) {
10690       for (f = 0; f < numFields; f++) {
10691         annz = 0;
10692         for (r = 0; r < rDof; r++) {
10693           a = anchors[rOff + r];
10694           if (a < sStart || a >= sEnd) continue;
10695           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10696           annz += aDof;
10697         }
10698         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10699         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10700         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10701       }
10702     } else {
10703       annz = 0;
10704       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10705       for (q = 0; q < dof; q++) {
10706         a = anchors[rOff + q];
10707         if (a < sStart || a >= sEnd) continue;
10708         PetscCall(PetscSectionGetDof(section, a, &aDof));
10709         annz += aDof;
10710       }
10711       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10712       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10713       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10714     }
10715   }
10716   nnz = i[m];
10717   PetscCall(PetscMalloc1(nnz, &j));
10718   offset = 0;
10719   for (p = pStart; p < pEnd; p++) {
10720     if (numFields) {
10721       for (f = 0; f < numFields; f++) {
10722         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10723         for (q = 0; q < dof; q++) {
10724           PetscInt rDof, rOff, r;
10725           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10726           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10727           for (r = 0; r < rDof; r++) {
10728             PetscInt s;
10729 
10730             a = anchors[rOff + r];
10731             if (a < sStart || a >= sEnd) continue;
10732             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10733             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10734             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10735           }
10736         }
10737       }
10738     } else {
10739       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10740       for (q = 0; q < dof; q++) {
10741         PetscInt rDof, rOff, r;
10742         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10743         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10744         for (r = 0; r < rDof; r++) {
10745           PetscInt s;
10746 
10747           a = anchors[rOff + r];
10748           if (a < sStart || a >= sEnd) continue;
10749           PetscCall(PetscSectionGetDof(section, a, &aDof));
10750           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10751           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10752         }
10753       }
10754     }
10755   }
10756   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10757   PetscCall(PetscFree(i));
10758   PetscCall(PetscFree(j));
10759   PetscCall(ISRestoreIndices(aIS, &anchors));
10760   PetscFunctionReturn(PETSC_SUCCESS);
10761 }
10762 
10763 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10764 {
10765   DM_Plex     *plex = (DM_Plex *)dm->data;
10766   PetscSection anchorSection, section, cSec;
10767   Mat          cMat;
10768 
10769   PetscFunctionBegin;
10770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10771   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10772   if (anchorSection) {
10773     PetscInt Nf;
10774 
10775     PetscCall(DMGetLocalSection(dm, &section));
10776     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10777     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10778     PetscCall(DMGetNumFields(dm, &Nf));
10779     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10780     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10781     PetscCall(PetscSectionDestroy(&cSec));
10782     PetscCall(MatDestroy(&cMat));
10783   }
10784   PetscFunctionReturn(PETSC_SUCCESS);
10785 }
10786 
10787 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10788 {
10789   IS           subis;
10790   PetscSection section, subsection;
10791 
10792   PetscFunctionBegin;
10793   PetscCall(DMGetLocalSection(dm, &section));
10794   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10795   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10796   /* Create subdomain */
10797   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10798   /* Create submodel */
10799   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10800   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10801   PetscCall(DMSetLocalSection(*subdm, subsection));
10802   PetscCall(PetscSectionDestroy(&subsection));
10803   PetscCall(DMCopyDisc(dm, *subdm));
10804   /* Create map from submodel to global model */
10805   if (is) {
10806     PetscSection    sectionGlobal, subsectionGlobal;
10807     IS              spIS;
10808     const PetscInt *spmap;
10809     PetscInt       *subIndices;
10810     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10811     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10812 
10813     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10814     PetscCall(ISGetIndices(spIS, &spmap));
10815     PetscCall(PetscSectionGetNumFields(section, &Nf));
10816     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10817     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10818     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10819     for (p = pStart; p < pEnd; ++p) {
10820       PetscInt gdof, pSubSize = 0;
10821 
10822       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10823       if (gdof > 0) {
10824         for (f = 0; f < Nf; ++f) {
10825           PetscInt fdof, fcdof;
10826 
10827           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10828           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10829           pSubSize += fdof - fcdof;
10830         }
10831         subSize += pSubSize;
10832         if (pSubSize) {
10833           if (bs < 0) {
10834             bs = pSubSize;
10835           } else if (bs != pSubSize) {
10836             /* Layout does not admit a pointwise block size */
10837             bs = 1;
10838           }
10839         }
10840       }
10841     }
10842     /* Must have same blocksize on all procs (some might have no points) */
10843     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10844     bsLocal[1] = bs;
10845     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10846     if (bsMinMax[0] != bsMinMax[1]) {
10847       bs = 1;
10848     } else {
10849       bs = bsMinMax[0];
10850     }
10851     PetscCall(PetscMalloc1(subSize, &subIndices));
10852     for (p = pStart; p < pEnd; ++p) {
10853       PetscInt gdof, goff;
10854 
10855       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10856       if (gdof > 0) {
10857         const PetscInt point = spmap[p];
10858 
10859         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10860         for (f = 0; f < Nf; ++f) {
10861           PetscInt fdof, fcdof, fc, f2, poff = 0;
10862 
10863           /* Can get rid of this loop by storing field information in the global section */
10864           for (f2 = 0; f2 < f; ++f2) {
10865             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10866             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10867             poff += fdof - fcdof;
10868           }
10869           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10870           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10871           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10872         }
10873       }
10874     }
10875     PetscCall(ISRestoreIndices(spIS, &spmap));
10876     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10877     if (bs > 1) {
10878       /* We need to check that the block size does not come from non-contiguous fields */
10879       PetscInt i, j, set = 1;
10880       for (i = 0; i < subSize; i += bs) {
10881         for (j = 0; j < bs; ++j) {
10882           if (subIndices[i + j] != subIndices[i] + j) {
10883             set = 0;
10884             break;
10885           }
10886         }
10887       }
10888       if (set) PetscCall(ISSetBlockSize(*is, bs));
10889     }
10890     // Attach nullspace
10891     if (dm->nullspaceConstructors) {
10892       for (f = 0; f < Nf; ++f) {
10893         (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10894         if ((*subdm)->nullspaceConstructors[f]) break;
10895       }
10896       if (f < Nf) {
10897         MatNullSpace nullSpace;
10898         PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10899 
10900         PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10901         PetscCall(MatNullSpaceDestroy(&nullSpace));
10902       }
10903     }
10904   }
10905   PetscFunctionReturn(PETSC_SUCCESS);
10906 }
10907 
10908 /*@
10909   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10910 
10911   Input Parameters:
10912 + dm    - The `DM`
10913 - dummy - unused argument
10914 
10915   Options Database Key:
10916 . -dm_plex_monitor_throughput - Activate the monitor
10917 
10918   Level: developer
10919 
10920 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10921 @*/
10922 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10923 {
10924   PetscLogHandler default_handler;
10925 
10926   PetscFunctionBegin;
10927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10928   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10929   if (default_handler) {
10930     PetscLogEvent      event;
10931     PetscEventPerfInfo eventInfo;
10932     PetscLogDouble     cellRate, flopRate;
10933     PetscInt           cStart, cEnd, Nf, N;
10934     const char        *name;
10935 
10936     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10937     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10938     PetscCall(DMGetNumFields(dm, &Nf));
10939     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10940     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10941     N        = (cEnd - cStart) * Nf * eventInfo.count;
10942     flopRate = eventInfo.flops / eventInfo.time;
10943     cellRate = N / eventInfo.time;
10944     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));
10945   } else {
10946     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.");
10947   }
10948   PetscFunctionReturn(PETSC_SUCCESS);
10949 }
10950