xref: /petsc/src/dm/impls/plex/plex.c (revision d52a580b706c59ca78066c1e38754e45b6b56e2b)
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(PetscArraycpy(&newarr[co], cone, cn));
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 support
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   This is used for getting the all values in a `Vec` in the closure of a mesh point.
6681   To get only the values in the closure of a mesh point at a specific depth (for example, at mesh vertices), use `DMPlexVecGetClosureAtDepth()`.
6682 
6683   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6684   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6685   assembly function, and a user may already have allocated storage for this operation.
6686 
6687   A typical use could be
6688 .vb
6689    values = NULL;
6690    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6691    for (cl = 0; cl < clSize; ++cl) {
6692      <Compute on closure>
6693    }
6694    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6695 .ve
6696   or
6697 .vb
6698    PetscMalloc1(clMaxSize, &values);
6699    for (p = pStart; p < pEnd; ++p) {
6700      clSize = clMaxSize;
6701      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6702      for (cl = 0; cl < clSize; ++cl) {
6703        <Compute on closure>
6704      }
6705    }
6706    PetscFree(values);
6707 .ve
6708 
6709   Fortran Notes:
6710   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6711   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6712 
6713   `values` must be declared with
6714 .vb
6715   PetscScalar,dimension(:),pointer   :: values
6716 .ve
6717   and it will be allocated internally by PETSc to hold the values returned
6718 
6719 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosureAtDepth()`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6720 @*/
6721 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6722 {
6723   PetscFunctionBeginHot;
6724   PetscCall(DMPlexVecGetOrientedClosure(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6725   PetscFunctionReturn(PETSC_SUCCESS);
6726 }
6727 
6728 /*@C
6729   DMPlexVecGetClosureAtDepth - Get an array of the values on the closure of 'point' that are at a specific depth
6730 
6731   Not collective
6732 
6733   Input Parameters:
6734 + dm      - The `DM`
6735 . section - The section describing the layout in `v`, or `NULL` to use the default section
6736 . v       - The local vector
6737 . depth   - The depth of mesh points that should be returned
6738 - point   - The point in the `DM`
6739 
6740   Input/Output Parameters:
6741 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6742 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6743            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6744 
6745   Level: intermediate
6746 
6747   Notes:
6748   This is used for getting the values in a `Vec` associated with specific mesh points.
6749   For example, to get only the values at mesh vertices, pass `depth=0`. To get all the values in the closure of a mesh point, use `DMPlexVecGetClosure()`.
6750 
6751   `DMPlexVecGetClosureAtDepth()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6752   calling function. This is because `DMPlexVecGetClosureAtDepth()` is typically called in the inner loop of a `Vec` or `Mat`
6753   assembly function, and a user may already have allocated storage for this operation.
6754 
6755   A typical use could be
6756 .vb
6757    values = NULL;
6758    PetscCall(DMPlexVecGetClosureAtDepth(dm, NULL, v, p, depth, &clSize, &values));
6759    for (cl = 0; cl < clSize; ++cl) {
6760      <Compute on closure>
6761    }
6762    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6763 .ve
6764   or
6765 .vb
6766    PetscMalloc1(clMaxSize, &values);
6767    for (p = pStart; p < pEnd; ++p) {
6768      clSize = clMaxSize;
6769      PetscCall(DMPlexVecGetClosureAtDepth(dm, NULL, v, p, depth, &clSize, &values));
6770      for (cl = 0; cl < clSize; ++cl) {
6771        <Compute on closure>
6772      }
6773    }
6774    PetscFree(values);
6775 .ve
6776 
6777   Fortran Notes:
6778   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6779   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6780 
6781   `values` must be declared with
6782 .vb
6783   PetscScalar,dimension(:),pointer   :: values
6784 .ve
6785   and it will be allocated internally by PETSc to hold the values returned
6786 
6787 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6788 @*/
6789 PetscErrorCode DMPlexVecGetClosureAtDepth(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6790 {
6791   DMLabel            depthLabel;
6792   PetscSection       clSection;
6793   IS                 clPoints;
6794   PetscScalar       *array;
6795   const PetscScalar *vArray;
6796   PetscInt          *points = NULL;
6797   const PetscInt    *clp, *perm = NULL;
6798   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6799 
6800   PetscFunctionBeginHot;
6801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6802   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6803   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6804   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6805   PetscCall(DMPlexGetDepth(dm, &mdepth));
6806   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6807   PetscCall(PetscSectionGetNumFields(section, &numFields));
6808   if (mdepth == 1 && numFields < 2) {
6809     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6810     PetscFunctionReturn(PETSC_SUCCESS);
6811   }
6812   /* Get points */
6813   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6814   for (clsize = 0, p = 0; p < Np; p++) {
6815     PetscInt dof;
6816     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6817     clsize += dof;
6818   }
6819   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6820   /* Filter points */
6821   for (p = 0; p < numPoints * 2; p += 2) {
6822     PetscInt dep;
6823 
6824     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6825     if (dep != depth) continue;
6826     points[Np * 2 + 0] = points[p];
6827     points[Np * 2 + 1] = points[p + 1];
6828     ++Np;
6829   }
6830   /* Get array */
6831   if (!values || !*values) {
6832     PetscInt asize = 0, dof;
6833 
6834     for (p = 0; p < Np * 2; p += 2) {
6835       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6836       asize += dof;
6837     }
6838     if (!values) {
6839       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6840       if (csize) *csize = asize;
6841       PetscFunctionReturn(PETSC_SUCCESS);
6842     }
6843     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6844   } else {
6845     array = *values;
6846   }
6847   PetscCall(VecGetArrayRead(v, &vArray));
6848   /* Get values */
6849   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6850   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6851   /* Cleanup points */
6852   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6853   /* Cleanup array */
6854   PetscCall(VecRestoreArrayRead(v, &vArray));
6855   if (!*values) {
6856     if (csize) *csize = size;
6857     *values = array;
6858   } else {
6859     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6860     *csize = size;
6861   }
6862   PetscFunctionReturn(PETSC_SUCCESS);
6863 }
6864 
6865 /*@C
6866   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6867 
6868   Not collective
6869 
6870   Input Parameters:
6871 + dm      - The `DM`
6872 . section - The section describing the layout in `v`, or `NULL` to use the default section
6873 . v       - The local vector
6874 . point   - The point in the `DM`
6875 . csize   - The number of values in the closure, or `NULL`
6876 - values  - The array of values
6877 
6878   Level: intermediate
6879 
6880   Note:
6881   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6882 
6883   Fortran Note:
6884   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6885   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6886 
6887 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6888 @*/
6889 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6890 {
6891   PetscInt size = 0;
6892 
6893   PetscFunctionBegin;
6894   /* Should work without recalculating size */
6895   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6896   *values = NULL;
6897   PetscFunctionReturn(PETSC_SUCCESS);
6898 }
6899 
6900 static inline void add(PetscScalar *x, PetscScalar y)
6901 {
6902   *x += y;
6903 }
6904 static inline void insert(PetscScalar *x, PetscScalar y)
6905 {
6906   *x = y;
6907 }
6908 
6909 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[])
6910 {
6911   PetscInt        cdof;  /* The number of constraints on this point */
6912   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6913   PetscScalar    *a;
6914   PetscInt        off, cind = 0, k;
6915 
6916   PetscFunctionBegin;
6917   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6918   PetscCall(PetscSectionGetOffset(section, point, &off));
6919   a = &array[off];
6920   if (!cdof || setBC) {
6921     if (clperm) {
6922       if (perm) {
6923         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6924       } else {
6925         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6926       }
6927     } else {
6928       if (perm) {
6929         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6930       } else {
6931         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6932       }
6933     }
6934   } else {
6935     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6936     if (clperm) {
6937       if (perm) {
6938         for (k = 0; k < dof; ++k) {
6939           if ((cind < cdof) && (k == cdofs[cind])) {
6940             ++cind;
6941             continue;
6942           }
6943           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6944         }
6945       } else {
6946         for (k = 0; k < dof; ++k) {
6947           if ((cind < cdof) && (k == cdofs[cind])) {
6948             ++cind;
6949             continue;
6950           }
6951           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6952         }
6953       }
6954     } else {
6955       if (perm) {
6956         for (k = 0; k < dof; ++k) {
6957           if ((cind < cdof) && (k == cdofs[cind])) {
6958             ++cind;
6959             continue;
6960           }
6961           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6962         }
6963       } else {
6964         for (k = 0; k < dof; ++k) {
6965           if ((cind < cdof) && (k == cdofs[cind])) {
6966             ++cind;
6967             continue;
6968           }
6969           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6970         }
6971       }
6972     }
6973   }
6974   PetscFunctionReturn(PETSC_SUCCESS);
6975 }
6976 
6977 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[])
6978 {
6979   PetscInt        cdof;  /* The number of constraints on this point */
6980   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6981   PetscScalar    *a;
6982   PetscInt        off, cind = 0, k;
6983 
6984   PetscFunctionBegin;
6985   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6986   PetscCall(PetscSectionGetOffset(section, point, &off));
6987   a = &array[off];
6988   if (cdof) {
6989     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6990     if (clperm) {
6991       if (perm) {
6992         for (k = 0; k < dof; ++k) {
6993           if ((cind < cdof) && (k == cdofs[cind])) {
6994             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6995             cind++;
6996           }
6997         }
6998       } else {
6999         for (k = 0; k < dof; ++k) {
7000           if ((cind < cdof) && (k == cdofs[cind])) {
7001             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
7002             cind++;
7003           }
7004         }
7005       }
7006     } else {
7007       if (perm) {
7008         for (k = 0; k < dof; ++k) {
7009           if ((cind < cdof) && (k == cdofs[cind])) {
7010             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
7011             cind++;
7012           }
7013         }
7014       } else {
7015         for (k = 0; k < dof; ++k) {
7016           if ((cind < cdof) && (k == cdofs[cind])) {
7017             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
7018             cind++;
7019           }
7020         }
7021       }
7022     }
7023   }
7024   PetscFunctionReturn(PETSC_SUCCESS);
7025 }
7026 
7027 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[])
7028 {
7029   PetscScalar    *a;
7030   PetscInt        fdof, foff, fcdof, foffset = *offset;
7031   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7032   PetscInt        cind = 0, b;
7033 
7034   PetscFunctionBegin;
7035   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7036   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
7037   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
7038   a = &array[foff];
7039   if (!fcdof || setBC) {
7040     if (clperm) {
7041       if (perm) {
7042         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7043       } else {
7044         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7045       }
7046     } else {
7047       if (perm) {
7048         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7049       } else {
7050         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7051       }
7052     }
7053   } else {
7054     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7055     if (clperm) {
7056       if (perm) {
7057         for (b = 0; b < fdof; b++) {
7058           if ((cind < fcdof) && (b == fcdofs[cind])) {
7059             ++cind;
7060             continue;
7061           }
7062           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7063         }
7064       } else {
7065         for (b = 0; b < fdof; b++) {
7066           if ((cind < fcdof) && (b == fcdofs[cind])) {
7067             ++cind;
7068             continue;
7069           }
7070           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7071         }
7072       }
7073     } else {
7074       if (perm) {
7075         for (b = 0; b < fdof; b++) {
7076           if ((cind < fcdof) && (b == fcdofs[cind])) {
7077             ++cind;
7078             continue;
7079           }
7080           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7081         }
7082       } else {
7083         for (b = 0; b < fdof; b++) {
7084           if ((cind < fcdof) && (b == fcdofs[cind])) {
7085             ++cind;
7086             continue;
7087           }
7088           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7089         }
7090       }
7091     }
7092   }
7093   *offset += fdof;
7094   PetscFunctionReturn(PETSC_SUCCESS);
7095 }
7096 
7097 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[])
7098 {
7099   PetscScalar    *a;
7100   PetscInt        fdof, foff, fcdof, foffset = *offset;
7101   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7102   PetscInt        Nc, cind = 0, ncind = 0, b;
7103   PetscBool       ncSet, fcSet;
7104 
7105   PetscFunctionBegin;
7106   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
7107   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7108   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
7109   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
7110   a = &array[foff];
7111   if (fcdof) {
7112     /* We just override fcdof and fcdofs with Ncc and comps */
7113     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7114     if (clperm) {
7115       if (perm) {
7116         if (comps) {
7117           for (b = 0; b < fdof; b++) {
7118             ncSet = fcSet = PETSC_FALSE;
7119             if (b % Nc == comps[ncind]) {
7120               ncind = (ncind + 1) % Ncc;
7121               ncSet = PETSC_TRUE;
7122             }
7123             if ((cind < fcdof) && (b == fcdofs[cind])) {
7124               ++cind;
7125               fcSet = PETSC_TRUE;
7126             }
7127             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7128           }
7129         } else {
7130           for (b = 0; b < fdof; b++) {
7131             if ((cind < fcdof) && (b == fcdofs[cind])) {
7132               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7133               ++cind;
7134             }
7135           }
7136         }
7137       } else {
7138         if (comps) {
7139           for (b = 0; b < fdof; b++) {
7140             ncSet = fcSet = PETSC_FALSE;
7141             if (b % Nc == comps[ncind]) {
7142               ncind = (ncind + 1) % Ncc;
7143               ncSet = PETSC_TRUE;
7144             }
7145             if ((cind < fcdof) && (b == fcdofs[cind])) {
7146               ++cind;
7147               fcSet = PETSC_TRUE;
7148             }
7149             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7150           }
7151         } else {
7152           for (b = 0; b < fdof; b++) {
7153             if ((cind < fcdof) && (b == fcdofs[cind])) {
7154               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7155               ++cind;
7156             }
7157           }
7158         }
7159       }
7160     } else {
7161       if (perm) {
7162         if (comps) {
7163           for (b = 0; b < fdof; b++) {
7164             ncSet = fcSet = PETSC_FALSE;
7165             if (b % Nc == comps[ncind]) {
7166               ncind = (ncind + 1) % Ncc;
7167               ncSet = PETSC_TRUE;
7168             }
7169             if ((cind < fcdof) && (b == fcdofs[cind])) {
7170               ++cind;
7171               fcSet = PETSC_TRUE;
7172             }
7173             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7174           }
7175         } else {
7176           for (b = 0; b < fdof; b++) {
7177             if ((cind < fcdof) && (b == fcdofs[cind])) {
7178               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7179               ++cind;
7180             }
7181           }
7182         }
7183       } else {
7184         if (comps) {
7185           for (b = 0; b < fdof; b++) {
7186             ncSet = fcSet = PETSC_FALSE;
7187             if (b % Nc == comps[ncind]) {
7188               ncind = (ncind + 1) % Ncc;
7189               ncSet = PETSC_TRUE;
7190             }
7191             if ((cind < fcdof) && (b == fcdofs[cind])) {
7192               ++cind;
7193               fcSet = PETSC_TRUE;
7194             }
7195             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7196           }
7197         } else {
7198           for (b = 0; b < fdof; b++) {
7199             if ((cind < fcdof) && (b == fcdofs[cind])) {
7200               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7201               ++cind;
7202             }
7203           }
7204         }
7205       }
7206     }
7207   }
7208   *offset += fdof;
7209   PetscFunctionReturn(PETSC_SUCCESS);
7210 }
7211 
7212 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7213 {
7214   PetscScalar    *array;
7215   const PetscInt *cone, *coneO;
7216   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7217 
7218   PetscFunctionBeginHot;
7219   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7220   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7221   PetscCall(DMPlexGetCone(dm, point, &cone));
7222   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7223   PetscCall(VecGetArray(v, &array));
7224   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7225     const PetscInt cp = !p ? point : cone[p - 1];
7226     const PetscInt o  = !p ? 0 : coneO[p - 1];
7227 
7228     if ((cp < pStart) || (cp >= pEnd)) {
7229       dof = 0;
7230       continue;
7231     }
7232     PetscCall(PetscSectionGetDof(section, cp, &dof));
7233     /* ADD_VALUES */
7234     {
7235       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7236       PetscScalar    *a;
7237       PetscInt        cdof, coff, cind = 0, k;
7238 
7239       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7240       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7241       a = &array[coff];
7242       if (!cdof) {
7243         if (o >= 0) {
7244           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7245         } else {
7246           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7247         }
7248       } else {
7249         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7250         if (o >= 0) {
7251           for (k = 0; k < dof; ++k) {
7252             if ((cind < cdof) && (k == cdofs[cind])) {
7253               ++cind;
7254               continue;
7255             }
7256             a[k] += values[off + k];
7257           }
7258         } else {
7259           for (k = 0; k < dof; ++k) {
7260             if ((cind < cdof) && (k == cdofs[cind])) {
7261               ++cind;
7262               continue;
7263             }
7264             a[k] += values[off + dof - k - 1];
7265           }
7266         }
7267       }
7268     }
7269   }
7270   PetscCall(VecRestoreArray(v, &array));
7271   PetscFunctionReturn(PETSC_SUCCESS);
7272 }
7273 
7274 /*@C
7275   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7276 
7277   Not collective
7278 
7279   Input Parameters:
7280 + dm      - The `DM`
7281 . section - The section describing the layout in `v`, or `NULL` to use the default section
7282 . v       - The local vector
7283 . point   - The point in the `DM`
7284 . values  - The array of values
7285 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7286             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7287 
7288   Level: intermediate
7289 
7290   Note:
7291   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7292 
7293 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7294 @*/
7295 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7296 {
7297   PetscSection    clSection;
7298   IS              clPoints;
7299   PetscScalar    *array;
7300   PetscInt       *points = NULL;
7301   const PetscInt *clp, *clperm = NULL;
7302   PetscInt        depth, numFields, numPoints, p, clsize;
7303 
7304   PetscFunctionBeginHot;
7305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7306   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7307   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7308   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7309   PetscCall(DMPlexGetDepth(dm, &depth));
7310   PetscCall(PetscSectionGetNumFields(section, &numFields));
7311   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7312     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7313     PetscFunctionReturn(PETSC_SUCCESS);
7314   }
7315   /* Get points */
7316   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7317   for (clsize = 0, p = 0; p < numPoints; p++) {
7318     PetscInt dof;
7319     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7320     clsize += dof;
7321   }
7322   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7323   /* Get array */
7324   PetscCall(VecGetArray(v, &array));
7325   /* Get values */
7326   if (numFields > 0) {
7327     PetscInt offset = 0, f;
7328     for (f = 0; f < numFields; ++f) {
7329       const PetscInt    **perms = NULL;
7330       const PetscScalar **flips = NULL;
7331 
7332       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7333       switch (mode) {
7334       case INSERT_VALUES:
7335         for (p = 0; p < numPoints; p++) {
7336           const PetscInt     point = points[2 * p];
7337           const PetscInt    *perm  = perms ? perms[p] : NULL;
7338           const PetscScalar *flip  = flips ? flips[p] : NULL;
7339           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7340         }
7341         break;
7342       case INSERT_ALL_VALUES:
7343         for (p = 0; p < numPoints; p++) {
7344           const PetscInt     point = points[2 * p];
7345           const PetscInt    *perm  = perms ? perms[p] : NULL;
7346           const PetscScalar *flip  = flips ? flips[p] : NULL;
7347           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7348         }
7349         break;
7350       case INSERT_BC_VALUES:
7351         for (p = 0; p < numPoints; p++) {
7352           const PetscInt     point = points[2 * p];
7353           const PetscInt    *perm  = perms ? perms[p] : NULL;
7354           const PetscScalar *flip  = flips ? flips[p] : NULL;
7355           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7356         }
7357         break;
7358       case ADD_VALUES:
7359         for (p = 0; p < numPoints; p++) {
7360           const PetscInt     point = points[2 * p];
7361           const PetscInt    *perm  = perms ? perms[p] : NULL;
7362           const PetscScalar *flip  = flips ? flips[p] : NULL;
7363           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7364         }
7365         break;
7366       case ADD_ALL_VALUES:
7367         for (p = 0; p < numPoints; p++) {
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(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7372         }
7373         break;
7374       case ADD_BC_VALUES:
7375         for (p = 0; p < numPoints; p++) {
7376           const PetscInt     point = points[2 * p];
7377           const PetscInt    *perm  = perms ? perms[p] : NULL;
7378           const PetscScalar *flip  = flips ? flips[p] : NULL;
7379           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7380         }
7381         break;
7382       default:
7383         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7384       }
7385       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7386     }
7387   } else {
7388     PetscInt            dof, off;
7389     const PetscInt    **perms = NULL;
7390     const PetscScalar **flips = NULL;
7391 
7392     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7393     switch (mode) {
7394     case INSERT_VALUES:
7395       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7396         const PetscInt     point = points[2 * p];
7397         const PetscInt    *perm  = perms ? perms[p] : NULL;
7398         const PetscScalar *flip  = flips ? flips[p] : NULL;
7399         PetscCall(PetscSectionGetDof(section, point, &dof));
7400         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7401       }
7402       break;
7403     case INSERT_ALL_VALUES:
7404       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7405         const PetscInt     point = points[2 * p];
7406         const PetscInt    *perm  = perms ? perms[p] : NULL;
7407         const PetscScalar *flip  = flips ? flips[p] : NULL;
7408         PetscCall(PetscSectionGetDof(section, point, &dof));
7409         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7410       }
7411       break;
7412     case INSERT_BC_VALUES:
7413       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7414         const PetscInt     point = points[2 * p];
7415         const PetscInt    *perm  = perms ? perms[p] : NULL;
7416         const PetscScalar *flip  = flips ? flips[p] : NULL;
7417         PetscCall(PetscSectionGetDof(section, point, &dof));
7418         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7419       }
7420       break;
7421     case ADD_VALUES:
7422       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7423         const PetscInt     point = points[2 * p];
7424         const PetscInt    *perm  = perms ? perms[p] : NULL;
7425         const PetscScalar *flip  = flips ? flips[p] : NULL;
7426         PetscCall(PetscSectionGetDof(section, point, &dof));
7427         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7428       }
7429       break;
7430     case ADD_ALL_VALUES:
7431       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7432         const PetscInt     point = points[2 * p];
7433         const PetscInt    *perm  = perms ? perms[p] : NULL;
7434         const PetscScalar *flip  = flips ? flips[p] : NULL;
7435         PetscCall(PetscSectionGetDof(section, point, &dof));
7436         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7437       }
7438       break;
7439     case ADD_BC_VALUES:
7440       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7441         const PetscInt     point = points[2 * p];
7442         const PetscInt    *perm  = perms ? perms[p] : NULL;
7443         const PetscScalar *flip  = flips ? flips[p] : NULL;
7444         PetscCall(PetscSectionGetDof(section, point, &dof));
7445         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7446       }
7447       break;
7448     default:
7449       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7450     }
7451     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7452   }
7453   /* Cleanup points */
7454   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7455   /* Cleanup array */
7456   PetscCall(VecRestoreArray(v, &array));
7457   PetscFunctionReturn(PETSC_SUCCESS);
7458 }
7459 
7460 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7461 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7462 {
7463   PetscFunctionBegin;
7464   *contains = PETSC_TRUE;
7465   if (label) {
7466     PetscInt fdof;
7467 
7468     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7469     if (!*contains) {
7470       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7471       *offset += fdof;
7472       PetscFunctionReturn(PETSC_SUCCESS);
7473     }
7474   }
7475   PetscFunctionReturn(PETSC_SUCCESS);
7476 }
7477 
7478 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7479 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)
7480 {
7481   PetscSection    clSection;
7482   IS              clPoints;
7483   PetscScalar    *array;
7484   PetscInt       *points = NULL;
7485   const PetscInt *clp;
7486   PetscInt        numFields, numPoints, p;
7487   PetscInt        offset = 0, f;
7488 
7489   PetscFunctionBeginHot;
7490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7491   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7492   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7493   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7494   PetscCall(PetscSectionGetNumFields(section, &numFields));
7495   /* Get points */
7496   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7497   /* Get array */
7498   PetscCall(VecGetArray(v, &array));
7499   /* Get values */
7500   for (f = 0; f < numFields; ++f) {
7501     const PetscInt    **perms = NULL;
7502     const PetscScalar **flips = NULL;
7503     PetscBool           contains;
7504 
7505     if (!fieldActive[f]) {
7506       for (p = 0; p < numPoints * 2; p += 2) {
7507         PetscInt fdof;
7508         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7509         offset += fdof;
7510       }
7511       continue;
7512     }
7513     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7514     switch (mode) {
7515     case INSERT_VALUES:
7516       for (p = 0; p < numPoints; p++) {
7517         const PetscInt     point = points[2 * p];
7518         const PetscInt    *perm  = perms ? perms[p] : NULL;
7519         const PetscScalar *flip  = flips ? flips[p] : NULL;
7520         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7521         if (!contains) continue;
7522         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7523       }
7524       break;
7525     case INSERT_ALL_VALUES:
7526       for (p = 0; p < numPoints; p++) {
7527         const PetscInt     point = points[2 * p];
7528         const PetscInt    *perm  = perms ? perms[p] : NULL;
7529         const PetscScalar *flip  = flips ? flips[p] : NULL;
7530         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7531         if (!contains) continue;
7532         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7533       }
7534       break;
7535     case INSERT_BC_VALUES:
7536       for (p = 0; p < numPoints; p++) {
7537         const PetscInt     point = points[2 * p];
7538         const PetscInt    *perm  = perms ? perms[p] : NULL;
7539         const PetscScalar *flip  = flips ? flips[p] : NULL;
7540         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7541         if (!contains) continue;
7542         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7543       }
7544       break;
7545     case ADD_VALUES:
7546       for (p = 0; p < numPoints; p++) {
7547         const PetscInt     point = points[2 * p];
7548         const PetscInt    *perm  = perms ? perms[p] : NULL;
7549         const PetscScalar *flip  = flips ? flips[p] : NULL;
7550         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7551         if (!contains) continue;
7552         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7553       }
7554       break;
7555     case ADD_ALL_VALUES:
7556       for (p = 0; p < numPoints; p++) {
7557         const PetscInt     point = points[2 * p];
7558         const PetscInt    *perm  = perms ? perms[p] : NULL;
7559         const PetscScalar *flip  = flips ? flips[p] : NULL;
7560         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7561         if (!contains) continue;
7562         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7563       }
7564       break;
7565     default:
7566       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7567     }
7568     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7569   }
7570   /* Cleanup points */
7571   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7572   /* Cleanup array */
7573   PetscCall(VecRestoreArray(v, &array));
7574   PetscFunctionReturn(PETSC_SUCCESS);
7575 }
7576 
7577 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7578 {
7579   PetscMPIInt rank;
7580   PetscInt    i, j;
7581 
7582   PetscFunctionBegin;
7583   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7584   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7585   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7586   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7587   numCIndices = numCIndices ? numCIndices : numRIndices;
7588   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7589   for (i = 0; i < numRIndices; i++) {
7590     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7591     for (j = 0; j < numCIndices; j++) {
7592 #if defined(PETSC_USE_COMPLEX)
7593       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7594 #else
7595       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7596 #endif
7597     }
7598     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7599   }
7600   PetscFunctionReturn(PETSC_SUCCESS);
7601 }
7602 
7603 /*
7604   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7605 
7606   Input Parameters:
7607 + section - The section for this data layout
7608 . islocal - Is the section (and thus indices being requested) local or global?
7609 . point   - The point contributing dofs with these indices
7610 . off     - The global offset of this point
7611 . loff    - The local offset of each field
7612 . setBC   - The flag determining whether to include indices of boundary values
7613 . perm    - A permutation of the dofs on this point, or NULL
7614 - indperm - A permutation of the entire indices array, or NULL
7615 
7616   Output Parameter:
7617 . indices - Indices for dofs on this point
7618 
7619   Level: developer
7620 
7621   Note: The indices could be local or global, depending on the value of 'off'.
7622 */
7623 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7624 {
7625   PetscInt        dof;   /* The number of unknowns on this point */
7626   PetscInt        cdof;  /* The number of constraints on this point */
7627   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7628   PetscInt        cind = 0, k;
7629 
7630   PetscFunctionBegin;
7631   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7632   PetscCall(PetscSectionGetDof(section, point, &dof));
7633   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7634   if (!cdof || setBC) {
7635     for (k = 0; k < dof; ++k) {
7636       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7637       const PetscInt ind    = indperm ? indperm[preind] : preind;
7638 
7639       indices[ind] = off + k;
7640     }
7641   } else {
7642     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7643     for (k = 0; k < dof; ++k) {
7644       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7645       const PetscInt ind    = indperm ? indperm[preind] : preind;
7646 
7647       if ((cind < cdof) && (k == cdofs[cind])) {
7648         /* Insert check for returning constrained indices */
7649         indices[ind] = -(off + k + 1);
7650         ++cind;
7651       } else {
7652         indices[ind] = off + k - (islocal ? 0 : cind);
7653       }
7654     }
7655   }
7656   *loff += dof;
7657   PetscFunctionReturn(PETSC_SUCCESS);
7658 }
7659 
7660 /*
7661  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7662 
7663  Input Parameters:
7664 + section - a section (global or local)
7665 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7666 . point - point within section
7667 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7668 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7669 . setBC - identify constrained (boundary condition) points via involution.
7670 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7671 . permsoff - offset
7672 - indperm - index permutation
7673 
7674  Output Parameter:
7675 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7676 . indices - array to hold indices (as defined by section) of each dof associated with point
7677 
7678  Notes:
7679  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7680  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7681  in the local vector.
7682 
7683  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7684  significant).  It is invalid to call with a global section and setBC=true.
7685 
7686  Developer Note:
7687  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7688  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7689  offset could be obtained from the section instead of passing it explicitly as we do now.
7690 
7691  Example:
7692  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7693  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7694  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7695  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.
7696 
7697  Level: developer
7698 */
7699 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[])
7700 {
7701   PetscInt numFields, foff, f;
7702 
7703   PetscFunctionBegin;
7704   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7705   PetscCall(PetscSectionGetNumFields(section, &numFields));
7706   for (f = 0, foff = 0; f < numFields; ++f) {
7707     PetscInt        fdof, cfdof;
7708     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7709     PetscInt        cind = 0, b;
7710     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7711 
7712     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7713     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7714     if (!cfdof || setBC) {
7715       for (b = 0; b < fdof; ++b) {
7716         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7717         const PetscInt ind    = indperm ? indperm[preind] : preind;
7718 
7719         indices[ind] = off + foff + b;
7720       }
7721     } else {
7722       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7723       for (b = 0; b < fdof; ++b) {
7724         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7725         const PetscInt ind    = indperm ? indperm[preind] : preind;
7726 
7727         if ((cind < cfdof) && (b == fcdofs[cind])) {
7728           indices[ind] = -(off + foff + b + 1);
7729           ++cind;
7730         } else {
7731           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7732         }
7733       }
7734     }
7735     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7736     foffs[f] += fdof;
7737   }
7738   PetscFunctionReturn(PETSC_SUCCESS);
7739 }
7740 
7741 /*
7742   This version believes the globalSection offsets for each field, rather than just the point offset
7743 
7744  . foffs - The offset into 'indices' for each field, since it is segregated by field
7745 
7746  Notes:
7747  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7748  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7749 */
7750 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7751 {
7752   PetscInt numFields, foff, f;
7753 
7754   PetscFunctionBegin;
7755   PetscCall(PetscSectionGetNumFields(section, &numFields));
7756   for (f = 0; f < numFields; ++f) {
7757     PetscInt        fdof, cfdof;
7758     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7759     PetscInt        cind = 0, b;
7760     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7761 
7762     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7763     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7764     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7765     if (!cfdof) {
7766       for (b = 0; b < fdof; ++b) {
7767         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7768         const PetscInt ind    = indperm ? indperm[preind] : preind;
7769 
7770         indices[ind] = foff + b;
7771       }
7772     } else {
7773       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7774       for (b = 0; b < fdof; ++b) {
7775         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7776         const PetscInt ind    = indperm ? indperm[preind] : preind;
7777 
7778         if ((cind < cfdof) && (b == fcdofs[cind])) {
7779           indices[ind] = -(foff + b + 1);
7780           ++cind;
7781         } else {
7782           indices[ind] = foff + b - cind;
7783         }
7784       }
7785     }
7786     foffs[f] += fdof;
7787   }
7788   PetscFunctionReturn(PETSC_SUCCESS);
7789 }
7790 
7791 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7792 {
7793   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7794 
7795   PetscFunctionBegin;
7796   PetscCall(PetscSectionGetNumFields(section, &numFields));
7797   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7798   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7799   for (PetscInt p = 0; p < nPoints; p++) {
7800     PetscInt     b       = pnts[2 * p];
7801     PetscInt     bSecDof = 0, bOff;
7802     PetscInt     cSecDof = 0;
7803     PetscSection indices_section;
7804 
7805     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7806     if (!bSecDof) continue;
7807     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7808     indices_section = cSecDof > 0 ? cSec : section;
7809     if (numFields) {
7810       PetscInt fStart[32], fEnd[32];
7811 
7812       fStart[0] = 0;
7813       fEnd[0]   = 0;
7814       for (PetscInt f = 0; f < numFields; f++) {
7815         PetscInt fDof = 0;
7816 
7817         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7818         fStart[f + 1] = fStart[f] + fDof;
7819         fEnd[f + 1]   = fStart[f + 1];
7820       }
7821       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7822       // only apply permutations on one side
7823       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7824       for (PetscInt f = 0; f < numFields; f++) {
7825         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7826       }
7827     } else {
7828       PetscInt bEnd = 0;
7829 
7830       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7831       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7832 
7833       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7834     }
7835   }
7836   PetscFunctionReturn(PETSC_SUCCESS);
7837 }
7838 
7839 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[])
7840 {
7841   Mat             cMat;
7842   PetscSection    aSec, cSec;
7843   IS              aIS;
7844   PetscInt        aStart = -1, aEnd = -1;
7845   PetscInt        sStart = -1, sEnd = -1;
7846   PetscInt        cStart = -1, cEnd = -1;
7847   const PetscInt *anchors;
7848   PetscInt        numFields, p;
7849   PetscInt        newNumPoints = 0, newNumIndices = 0;
7850   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7851   PetscInt        oldOffsets[32];
7852   PetscInt        newOffsets[32];
7853   PetscInt        oldOffsetsCopy[32];
7854   PetscInt        newOffsetsCopy[32];
7855   PetscScalar    *modMat         = NULL;
7856   PetscBool       anyConstrained = PETSC_FALSE;
7857 
7858   PetscFunctionBegin;
7859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7860   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7861   PetscCall(PetscSectionGetNumFields(section, &numFields));
7862 
7863   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7864   /* if there are point-to-point constraints */
7865   if (aSec) {
7866     PetscCall(PetscArrayzero(newOffsets, 32));
7867     PetscCall(PetscArrayzero(oldOffsets, 32));
7868     PetscCall(ISGetIndices(aIS, &anchors));
7869     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7870     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7871     /* figure out how many points are going to be in the new element matrix
7872      * (we allow double counting, because it's all just going to be summed
7873      * into the global matrix anyway) */
7874     for (p = 0; p < 2 * numPoints; p += 2) {
7875       PetscInt b    = points[p];
7876       PetscInt bDof = 0, bSecDof = 0;
7877 
7878       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7879       if (!bSecDof) continue;
7880 
7881       for (PetscInt f = 0; f < numFields; f++) {
7882         PetscInt fDof = 0;
7883 
7884         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7885         oldOffsets[f + 1] += fDof;
7886       }
7887       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7888       if (bDof) {
7889         /* this point is constrained */
7890         /* it is going to be replaced by its anchors */
7891         PetscInt bOff, q;
7892 
7893         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7894         for (q = 0; q < bDof; q++) {
7895           PetscInt a    = anchors[bOff + q];
7896           PetscInt aDof = 0;
7897 
7898           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7899           if (aDof) {
7900             anyConstrained = PETSC_TRUE;
7901             newNumPoints += 1;
7902           }
7903           newNumIndices += aDof;
7904           for (PetscInt f = 0; f < numFields; ++f) {
7905             PetscInt fDof = 0;
7906 
7907             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7908             newOffsets[f + 1] += fDof;
7909           }
7910         }
7911       } else {
7912         /* this point is not constrained */
7913         newNumPoints++;
7914         newNumIndices += bSecDof;
7915         for (PetscInt f = 0; f < numFields; ++f) {
7916           PetscInt fDof;
7917 
7918           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7919           newOffsets[f + 1] += fDof;
7920         }
7921       }
7922     }
7923   }
7924   if (!anyConstrained) {
7925     if (outNumPoints) *outNumPoints = 0;
7926     if (outNumIndices) *outNumIndices = 0;
7927     if (outPoints) *outPoints = NULL;
7928     if (outMat) *outMat = NULL;
7929     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7930     PetscFunctionReturn(PETSC_SUCCESS);
7931   }
7932 
7933   if (outNumPoints) *outNumPoints = newNumPoints;
7934   if (outNumIndices) *outNumIndices = newNumIndices;
7935 
7936   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7937   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7938 
7939   if (!outPoints && !outMat) {
7940     if (offsets) {
7941       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7942     }
7943     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7944     PetscFunctionReturn(PETSC_SUCCESS);
7945   }
7946 
7947   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7948   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7949 
7950   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7951   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7952 
7953   /* output arrays */
7954   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7955   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7956 
7957   // get the new Points
7958   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7959     PetscInt b    = points[2 * p];
7960     PetscInt bDof = 0, bSecDof = 0, bOff;
7961 
7962     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7963     if (!bSecDof) continue;
7964     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7965     if (bDof) {
7966       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7967       for (PetscInt q = 0; q < bDof; q++) {
7968         PetscInt a = anchors[bOff + q], aDof = 0;
7969 
7970         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7971         if (aDof) {
7972           newPoints[2 * newP]     = a;
7973           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7974           newP++;
7975         }
7976       }
7977     } else {
7978       newPoints[2 * newP]     = b;
7979       newPoints[2 * newP + 1] = points[2 * p + 1];
7980       newP++;
7981     }
7982   }
7983 
7984   if (outMat) {
7985     PetscScalar *tmpMat;
7986     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7987     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7988 
7989     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7990     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7991     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7992     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7993 
7994     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7995     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7996 
7997     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7998     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7999 
8000     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
8001     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
8002     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
8003     // for each field, insert the anchor modification into modMat
8004     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
8005       PetscInt fStart    = oldOffsets[f];
8006       PetscInt fNewStart = newOffsets[f];
8007       for (PetscInt p = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
8008         PetscInt b    = points[2 * p];
8009         PetscInt bDof = 0, bSecDof = 0, bOff;
8010 
8011         if (b >= sStart && b < sEnd) {
8012           if (numFields) {
8013             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
8014           } else {
8015             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
8016           }
8017         }
8018         if (!bSecDof) continue;
8019         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
8020         if (bDof) {
8021           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
8022           for (PetscInt q = 0; q < bDof; q++) {
8023             PetscInt a = anchors[bOff + q], aDof = 0;
8024 
8025             if (a >= sStart && a < sEnd) {
8026               if (numFields) {
8027                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
8028               } else {
8029                 PetscCall(PetscSectionGetDof(section, a, &aDof));
8030               }
8031             }
8032             if (aDof) {
8033               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
8034               for (PetscInt d = 0; d < bSecDof; d++) {
8035                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
8036               }
8037             }
8038             oNew += aDof;
8039           }
8040         } else {
8041           // Insert the identity matrix in this block
8042           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
8043           oNew += bSecDof;
8044         }
8045         o += bSecDof;
8046       }
8047     }
8048 
8049     *outMat = modMat;
8050 
8051     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
8052     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
8053     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
8054     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
8055     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
8056   }
8057   PetscCall(ISRestoreIndices(aIS, &anchors));
8058 
8059   /* output */
8060   if (outPoints) {
8061     *outPoints = newPoints;
8062   } else {
8063     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
8064   }
8065   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
8066   PetscFunctionReturn(PETSC_SUCCESS);
8067 }
8068 
8069 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)
8070 {
8071   PetscScalar *modMat        = NULL;
8072   PetscInt     newNumIndices = -1;
8073 
8074   PetscFunctionBegin;
8075   /* 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.
8076      modMat is that matrix C */
8077   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
8078   if (outNumIndices) *outNumIndices = newNumIndices;
8079   if (modMat) {
8080     const PetscScalar *newValues = values;
8081 
8082     if (multiplyRight) {
8083       PetscScalar *newNewValues = NULL;
8084       PetscBLASInt M, N, K;
8085       PetscScalar  a = 1.0, b = 0.0;
8086 
8087       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);
8088 
8089       PetscCall(PetscBLASIntCast(newNumIndices, &M));
8090       PetscCall(PetscBLASIntCast(numRows, &N));
8091       PetscCall(PetscBLASIntCast(numIndices, &K));
8092       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
8093       // row-major to column-major conversion, right multiplication becomes left multiplication
8094       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
8095       numCols   = newNumIndices;
8096       newValues = newNewValues;
8097     }
8098 
8099     if (multiplyLeft) {
8100       PetscScalar *newNewValues = NULL;
8101       PetscBLASInt M, N, K;
8102       PetscScalar  a = 1.0, b = 0.0;
8103 
8104       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);
8105 
8106       PetscCall(PetscBLASIntCast(numCols, &M));
8107       PetscCall(PetscBLASIntCast(newNumIndices, &N));
8108       PetscCall(PetscBLASIntCast(numIndices, &K));
8109       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
8110       // row-major to column-major conversion, left multiplication becomes right multiplication
8111       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
8112       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
8113       newValues = newNewValues;
8114     }
8115     *outValues = (PetscScalar *)newValues;
8116     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
8117   }
8118   PetscFunctionReturn(PETSC_SUCCESS);
8119 }
8120 
8121 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)
8122 {
8123   PetscFunctionBegin;
8124   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8125   PetscFunctionReturn(PETSC_SUCCESS);
8126 }
8127 
8128 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8129 {
8130   /* Closure ordering */
8131   PetscSection    clSection;
8132   IS              clPoints;
8133   const PetscInt *clp;
8134   PetscInt       *points;
8135   PetscInt        Ncl, Ni = 0;
8136 
8137   PetscFunctionBeginHot;
8138   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8139   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8140     PetscInt dof;
8141 
8142     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8143     Ni += dof;
8144   }
8145   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8146   *closureSize = Ni;
8147   PetscFunctionReturn(PETSC_SUCCESS);
8148 }
8149 
8150 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)
8151 {
8152   /* Closure ordering */
8153   PetscSection    clSection;
8154   IS              clPoints;
8155   const PetscInt *clp;
8156   PetscInt       *points;
8157   const PetscInt *clperm = NULL;
8158   /* Dof permutation and sign flips */
8159   const PetscInt    **perms[32] = {NULL};
8160   const PetscScalar **flips[32] = {NULL};
8161   PetscScalar        *valCopy   = NULL;
8162   /* Hanging node constraints */
8163   PetscInt    *pointsC = NULL;
8164   PetscScalar *valuesC = NULL;
8165   PetscInt     NclC, NiC;
8166 
8167   PetscInt *idx;
8168   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8169   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8170   PetscInt  idxStart, idxEnd;
8171   PetscInt  nRows, nCols;
8172 
8173   PetscFunctionBeginHot;
8174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8175   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8176   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8177   PetscAssertPointer(numRows, 6);
8178   PetscAssertPointer(numCols, 7);
8179   if (indices) PetscAssertPointer(indices, 8);
8180   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8181   if (values) PetscAssertPointer(values, 10);
8182   PetscCall(PetscSectionGetNumFields(section, &Nf));
8183   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8184   PetscCall(PetscArrayzero(offsets, 32));
8185   /* 1) Get points in closure */
8186   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8187   if (useClPerm) {
8188     PetscInt depth, clsize;
8189     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8190     for (clsize = 0, p = 0; p < Ncl; p++) {
8191       PetscInt dof;
8192       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8193       clsize += dof;
8194     }
8195     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8196   }
8197   /* 2) Get number of indices on these points and field offsets from section */
8198   for (p = 0; p < Ncl * 2; p += 2) {
8199     PetscInt dof, fdof;
8200 
8201     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8202     for (f = 0; f < Nf; ++f) {
8203       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8204       offsets[f + 1] += fdof;
8205     }
8206     Ni += dof;
8207   }
8208   if (*numRows == -1) *numRows = Ni;
8209   if (*numCols == -1) *numCols = Ni;
8210   nRows = *numRows;
8211   nCols = *numCols;
8212   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8213   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8214   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8215   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8216   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8217   for (f = 0; f < PetscMax(1, Nf); ++f) {
8218     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8219     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8220     /* may need to apply sign changes to the element matrix */
8221     if (values && flips[f]) {
8222       PetscInt foffset = offsets[f];
8223 
8224       for (p = 0; p < Ncl; ++p) {
8225         PetscInt           pnt  = points[2 * p], fdof;
8226         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8227 
8228         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8229         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8230         if (flip) {
8231           PetscInt i, j, k;
8232 
8233           if (!valCopy) {
8234             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8235             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8236             *values = valCopy;
8237           }
8238           for (i = 0; i < fdof; ++i) {
8239             PetscScalar fval = flip[i];
8240 
8241             if (multiplyRight) {
8242               for (k = 0; k < nRows; ++k) valCopy[Ni * k + (foffset + i)] *= fval;
8243             }
8244             if (multiplyLeft) {
8245               for (k = 0; k < nCols; ++k) valCopy[nCols * (foffset + i) + k] *= fval;
8246             }
8247           }
8248         }
8249         foffset += fdof;
8250       }
8251     }
8252   }
8253   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8254   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8255   if (NclC) {
8256     if (multiplyRight) *numCols = NiC;
8257     if (multiplyLeft) *numRows = NiC;
8258     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8259     for (f = 0; f < PetscMax(1, Nf); ++f) {
8260       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8261       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8262     }
8263     for (f = 0; f < PetscMax(1, Nf); ++f) {
8264       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8265       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8266     }
8267     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8268     Ncl    = NclC;
8269     Ni     = NiC;
8270     points = pointsC;
8271     if (values) *values = valuesC;
8272   }
8273   /* 5) Calculate indices */
8274   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8275   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8276   if (Nf) {
8277     PetscInt  idxOff;
8278     PetscBool useFieldOffsets;
8279 
8280     if (outOffsets) {
8281       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8282     }
8283     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8284     if (useFieldOffsets) {
8285       for (p = 0; p < Ncl; ++p) {
8286         const PetscInt pnt = points[p * 2];
8287 
8288         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8289       }
8290     } else {
8291       for (p = 0; p < Ncl; ++p) {
8292         const PetscInt pnt = points[p * 2];
8293 
8294         if (pnt < idxStart || pnt >= idxEnd) continue;
8295         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8296         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8297          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8298          * global section. */
8299         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8300       }
8301     }
8302   } else {
8303     PetscInt off = 0, idxOff;
8304 
8305     for (p = 0; p < Ncl; ++p) {
8306       const PetscInt  pnt  = points[p * 2];
8307       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8308 
8309       if (pnt < idxStart || pnt >= idxEnd) continue;
8310       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8311       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8312        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8313       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8314     }
8315   }
8316   /* 6) Cleanup */
8317   for (f = 0; f < PetscMax(1, Nf); ++f) {
8318     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8319     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8320   }
8321   if (NclC) {
8322     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8323   } else {
8324     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8325   }
8326 
8327   if (indices) *indices = idx;
8328   PetscFunctionReturn(PETSC_SUCCESS);
8329 }
8330 
8331 /*@C
8332   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8333 
8334   Not collective
8335 
8336   Input Parameters:
8337 + dm         - The `DM`
8338 . section    - The `PetscSection` describing the points (a local section)
8339 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8340 . point      - The point defining the closure
8341 - useClPerm  - Use the closure point permutation if available
8342 
8343   Output Parameters:
8344 + numIndices - The number of dof indices in the closure of point with the input sections
8345 . indices    - The dof indices
8346 . outOffsets - Array, of length the number of fields plus 1, to write the field offsets into, or `NULL`
8347 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8348 
8349   Level: advanced
8350 
8351   Notes:
8352   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8353 
8354   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8355   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8356   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8357   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8358   indices (with the above semantics) are implied.
8359 
8360 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8361           `PetscSection`, `DMGetGlobalSection()`
8362 @*/
8363 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8364 {
8365   PetscInt numRows = -1, numCols = -1;
8366 
8367   PetscFunctionBeginHot;
8368   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8369   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8370   *numIndices = numRows;
8371   PetscFunctionReturn(PETSC_SUCCESS);
8372 }
8373 
8374 /*@C
8375   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8376 
8377   Not collective
8378 
8379   Input Parameters:
8380 + dm         - The `DM`
8381 . section    - The `PetscSection` describing the points (a local section)
8382 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8383 . point      - The point defining the closure
8384 - useClPerm  - Use the closure point permutation if available
8385 
8386   Output Parameters:
8387 + numIndices - The number of dof indices in the closure of point with the input sections
8388 . indices    - The dof indices
8389 . outOffsets - Array to write the field offsets into, or `NULL`
8390 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8391 
8392   Level: advanced
8393 
8394   Notes:
8395   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8396 
8397   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8398   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8399   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8400   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8401   indices (with the above semantics) are implied.
8402 
8403 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8404 @*/
8405 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8406 {
8407   PetscFunctionBegin;
8408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8409   PetscAssertPointer(indices, 7);
8410   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8411   PetscFunctionReturn(PETSC_SUCCESS);
8412 }
8413 
8414 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8415 {
8416   DM_Plex           *mesh = (DM_Plex *)dm->data;
8417   PetscInt          *indices;
8418   PetscInt           numIndices;
8419   const PetscScalar *valuesOrig = values;
8420   PetscErrorCode     ierr;
8421 
8422   PetscFunctionBegin;
8423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8424   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8425   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8426   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8427   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8428   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8429 
8430   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8431 
8432   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8433   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8434   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8435   if (ierr) {
8436     PetscMPIInt rank;
8437 
8438     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8439     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8440     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8441     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8442     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8443     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8444   }
8445   if (mesh->printFEM > 1) {
8446     PetscInt i;
8447     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8448     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8449     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8450   }
8451 
8452   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8453   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8454   PetscFunctionReturn(PETSC_SUCCESS);
8455 }
8456 
8457 /*@C
8458   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8459 
8460   Not collective
8461 
8462   Input Parameters:
8463 + dm            - The `DM`
8464 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8465 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8466 . A             - The matrix
8467 . point         - The point in the `DM`
8468 . values        - The array of values
8469 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8470 
8471   Level: intermediate
8472 
8473 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8474 @*/
8475 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8476 {
8477   PetscFunctionBegin;
8478   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8479   PetscFunctionReturn(PETSC_SUCCESS);
8480 }
8481 
8482 /*@C
8483   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8484 
8485   Not collective
8486 
8487   Input Parameters:
8488 + dmRow            - The `DM` for the row fields
8489 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8490 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8491 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8492 . dmCol            - The `DM` for the column fields
8493 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8494 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8495 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8496 . A                - The matrix
8497 . point            - The point in the `DM`
8498 . values           - The array of values
8499 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8500 
8501   Level: intermediate
8502 
8503 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8504 @*/
8505 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)
8506 {
8507   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8508   PetscInt          *indicesRow, *indicesCol;
8509   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8510   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8511 
8512   PetscErrorCode ierr;
8513 
8514   PetscFunctionBegin;
8515   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8516   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8517   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8518   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8519   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8520   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8521   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8522   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8523   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8524   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8525   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8526 
8527   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8528   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8529   valuesV1 = valuesV0;
8530   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8531   valuesV2 = valuesV1;
8532   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8533 
8534   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8535   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8536   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8537   if (ierr) {
8538     PetscMPIInt rank;
8539 
8540     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8541     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8542     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8543     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8544     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8545     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8546     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8547   }
8548 
8549   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8550   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8551   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8552   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8553   PetscFunctionReturn(PETSC_SUCCESS);
8554 }
8555 
8556 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8557 {
8558   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8559   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8560   PetscInt       *cpoints = NULL;
8561   PetscInt       *findices, *cindices;
8562   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8563   PetscInt        foffsets[32], coffsets[32];
8564   DMPolytopeType  ct;
8565   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8566   PetscErrorCode  ierr;
8567 
8568   PetscFunctionBegin;
8569   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8570   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8571   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8572   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8573   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8574   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8575   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8576   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8577   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8578   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8579   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8580   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8581   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8582   PetscCall(PetscArrayzero(foffsets, 32));
8583   PetscCall(PetscArrayzero(coffsets, 32));
8584   /* Column indices */
8585   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8586   maxFPoints = numCPoints;
8587   /* Compress out points not in the section */
8588   /*   TODO: Squeeze out points with 0 dof as well */
8589   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8590   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8591     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8592       cpoints[q * 2]     = cpoints[p];
8593       cpoints[q * 2 + 1] = cpoints[p + 1];
8594       ++q;
8595     }
8596   }
8597   numCPoints = q;
8598   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8599     PetscInt fdof;
8600 
8601     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8602     if (!dof) continue;
8603     for (f = 0; f < numFields; ++f) {
8604       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8605       coffsets[f + 1] += fdof;
8606     }
8607     numCIndices += dof;
8608   }
8609   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8610   /* Row indices */
8611   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8612   {
8613     DMPlexTransform tr;
8614     DMPolytopeType *rct;
8615     PetscInt       *rsize, *rcone, *rornt, Nt;
8616 
8617     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8618     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8619     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8620     numSubcells = rsize[Nt - 1];
8621     PetscCall(DMPlexTransformDestroy(&tr));
8622   }
8623   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8624   for (r = 0, q = 0; r < numSubcells; ++r) {
8625     /* TODO Map from coarse to fine cells */
8626     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8627     /* Compress out points not in the section */
8628     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8629     for (p = 0; p < numFPoints * 2; p += 2) {
8630       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8631         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8632         if (!dof) continue;
8633         for (s = 0; s < q; ++s)
8634           if (fpoints[p] == ftotpoints[s * 2]) break;
8635         if (s < q) continue;
8636         ftotpoints[q * 2]     = fpoints[p];
8637         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8638         ++q;
8639       }
8640     }
8641     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8642   }
8643   numFPoints = q;
8644   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8645     PetscInt fdof;
8646 
8647     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8648     if (!dof) continue;
8649     for (f = 0; f < numFields; ++f) {
8650       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8651       foffsets[f + 1] += fdof;
8652     }
8653     numFIndices += dof;
8654   }
8655   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8656 
8657   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8658   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8659   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8660   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8661   if (numFields) {
8662     const PetscInt **permsF[32] = {NULL};
8663     const PetscInt **permsC[32] = {NULL};
8664 
8665     for (f = 0; f < numFields; f++) {
8666       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8667       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8668     }
8669     for (p = 0; p < numFPoints; p++) {
8670       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8671       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8672     }
8673     for (p = 0; p < numCPoints; p++) {
8674       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8675       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8676     }
8677     for (f = 0; f < numFields; f++) {
8678       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8679       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8680     }
8681   } else {
8682     const PetscInt **permsF = NULL;
8683     const PetscInt **permsC = NULL;
8684 
8685     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8686     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8687     for (p = 0, off = 0; p < numFPoints; p++) {
8688       const PetscInt *perm = permsF ? permsF[p] : NULL;
8689 
8690       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8691       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8692     }
8693     for (p = 0, off = 0; p < numCPoints; p++) {
8694       const PetscInt *perm = permsC ? permsC[p] : NULL;
8695 
8696       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8697       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8698     }
8699     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8700     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8701   }
8702   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8703   /* TODO: flips */
8704   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8705   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8706   if (ierr) {
8707     PetscMPIInt rank;
8708 
8709     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8710     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8711     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8712     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8713     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8714   }
8715   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8716   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8717   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8718   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8719   PetscFunctionReturn(PETSC_SUCCESS);
8720 }
8721 
8722 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8723 {
8724   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8725   PetscInt       *cpoints      = NULL;
8726   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8727   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8728   DMPolytopeType  ct;
8729   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8730 
8731   PetscFunctionBegin;
8732   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8733   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8734   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8735   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8736   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8737   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8738   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8739   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8740   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8741   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8742   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8743   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8744   /* Column indices */
8745   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8746   maxFPoints = numCPoints;
8747   /* Compress out points not in the section */
8748   /*   TODO: Squeeze out points with 0 dof as well */
8749   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8750   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8751     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8752       cpoints[q * 2]     = cpoints[p];
8753       cpoints[q * 2 + 1] = cpoints[p + 1];
8754       ++q;
8755     }
8756   }
8757   numCPoints = q;
8758   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8759     PetscInt fdof;
8760 
8761     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8762     if (!dof) continue;
8763     for (f = 0; f < numFields; ++f) {
8764       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8765       coffsets[f + 1] += fdof;
8766     }
8767     numCIndices += dof;
8768   }
8769   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8770   /* Row indices */
8771   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8772   {
8773     DMPlexTransform tr;
8774     DMPolytopeType *rct;
8775     PetscInt       *rsize, *rcone, *rornt, Nt;
8776 
8777     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8778     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8779     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8780     numSubcells = rsize[Nt - 1];
8781     PetscCall(DMPlexTransformDestroy(&tr));
8782   }
8783   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8784   for (r = 0, q = 0; r < numSubcells; ++r) {
8785     /* TODO Map from coarse to fine cells */
8786     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8787     /* Compress out points not in the section */
8788     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8789     for (p = 0; p < numFPoints * 2; p += 2) {
8790       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8791         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8792         if (!dof) continue;
8793         for (s = 0; s < q; ++s)
8794           if (fpoints[p] == ftotpoints[s * 2]) break;
8795         if (s < q) continue;
8796         ftotpoints[q * 2]     = fpoints[p];
8797         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8798         ++q;
8799       }
8800     }
8801     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8802   }
8803   numFPoints = q;
8804   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8805     PetscInt fdof;
8806 
8807     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8808     if (!dof) continue;
8809     for (f = 0; f < numFields; ++f) {
8810       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8811       foffsets[f + 1] += fdof;
8812     }
8813     numFIndices += dof;
8814   }
8815   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8816 
8817   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8818   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8819   if (numFields) {
8820     const PetscInt **permsF[32] = {NULL};
8821     const PetscInt **permsC[32] = {NULL};
8822 
8823     for (f = 0; f < numFields; f++) {
8824       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8825       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8826     }
8827     for (p = 0; p < numFPoints; p++) {
8828       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8829       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8830     }
8831     for (p = 0; p < numCPoints; p++) {
8832       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8833       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8834     }
8835     for (f = 0; f < numFields; f++) {
8836       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8837       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8838     }
8839   } else {
8840     const PetscInt **permsF = NULL;
8841     const PetscInt **permsC = NULL;
8842 
8843     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8844     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8845     for (p = 0, off = 0; p < numFPoints; p++) {
8846       const PetscInt *perm = permsF ? permsF[p] : NULL;
8847 
8848       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8849       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8850     }
8851     for (p = 0, off = 0; p < numCPoints; p++) {
8852       const PetscInt *perm = permsC ? permsC[p] : NULL;
8853 
8854       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8855       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8856     }
8857     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8858     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8859   }
8860   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8861   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8862   PetscFunctionReturn(PETSC_SUCCESS);
8863 }
8864 
8865 /*@
8866   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8867 
8868   Input Parameter:
8869 . dm - The `DMPLEX` object
8870 
8871   Output Parameter:
8872 . cellHeight - The height of a cell
8873 
8874   Level: developer
8875 
8876 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8877 @*/
8878 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8879 {
8880   DM_Plex *mesh = (DM_Plex *)dm->data;
8881 
8882   PetscFunctionBegin;
8883   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8884   PetscAssertPointer(cellHeight, 2);
8885   *cellHeight = mesh->vtkCellHeight;
8886   PetscFunctionReturn(PETSC_SUCCESS);
8887 }
8888 
8889 /*@
8890   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8891 
8892   Input Parameters:
8893 + dm         - The `DMPLEX` object
8894 - cellHeight - The height of a cell
8895 
8896   Level: developer
8897 
8898 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8899 @*/
8900 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8901 {
8902   DM_Plex *mesh = (DM_Plex *)dm->data;
8903 
8904   PetscFunctionBegin;
8905   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8906   mesh->vtkCellHeight = cellHeight;
8907   PetscFunctionReturn(PETSC_SUCCESS);
8908 }
8909 
8910 /*@
8911   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8912 
8913   Input Parameters:
8914 + dm - The `DMPLEX` object
8915 - ct - The `DMPolytopeType` of the cell
8916 
8917   Output Parameters:
8918 + start - The first cell of this type, or `NULL`
8919 - end   - The upper bound on this celltype, or `NULL`
8920 
8921   Level: advanced
8922 
8923 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8924 @*/
8925 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8926 {
8927   DM_Plex *mesh = (DM_Plex *)dm->data;
8928   DMLabel  label;
8929   PetscInt pStart, pEnd;
8930 
8931   PetscFunctionBegin;
8932   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8933   if (start) {
8934     PetscAssertPointer(start, 3);
8935     *start = 0;
8936   }
8937   if (end) {
8938     PetscAssertPointer(end, 4);
8939     *end = 0;
8940   }
8941   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8942   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8943   if (mesh->tr) {
8944     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8945   } else {
8946     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8947     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8948     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8949   }
8950   PetscFunctionReturn(PETSC_SUCCESS);
8951 }
8952 
8953 /*@
8954   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8955 
8956   Input Parameters:
8957 + dm    - The `DMPLEX` object
8958 - depth - The depth for the given point stratum
8959 
8960   Output Parameter:
8961 . gsize - The global number of points in the stratum
8962 
8963   Level: advanced
8964 
8965 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8966 @*/
8967 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8968 {
8969   PetscSF         sf;
8970   const PetscInt *leaves;
8971   PetscInt        Nl, loc, start, end, lsize = 0;
8972 
8973   PetscFunctionBegin;
8974   PetscCall(DMGetPointSF(dm, &sf));
8975   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8976   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8977   for (PetscInt p = start; p < end; ++p) {
8978     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8979     if (loc < 0) ++lsize;
8980   }
8981   PetscCallMPI(MPIU_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8982   PetscFunctionReturn(PETSC_SUCCESS);
8983 }
8984 
8985 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8986 {
8987   PetscSection section, globalSection;
8988   PetscInt    *numbers, p;
8989 
8990   PetscFunctionBegin;
8991   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8992   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8993   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8994   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8995   PetscCall(PetscSectionSetUp(section));
8996   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8997   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8998   for (p = pStart; p < pEnd; ++p) {
8999     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
9000     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
9001     else numbers[p - pStart] += shift;
9002   }
9003   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
9004   if (globalSize) {
9005     PetscLayout layout;
9006     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
9007     PetscCall(PetscLayoutGetSize(layout, globalSize));
9008     PetscCall(PetscLayoutDestroy(&layout));
9009   }
9010   PetscCall(PetscSectionDestroy(&section));
9011   PetscCall(PetscSectionDestroy(&globalSection));
9012   PetscFunctionReturn(PETSC_SUCCESS);
9013 }
9014 
9015 /*@
9016   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
9017 
9018   Input Parameters:
9019 + dm         - The `DMPLEX` object
9020 - includeAll - Whether to include all cells, or just the simplex and box cells
9021 
9022   Output Parameter:
9023 . globalCellNumbers - Global cell numbers for all cells on this process
9024 
9025   Level: developer
9026 
9027 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
9028 @*/
9029 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
9030 {
9031   PetscInt cellHeight, cStart, cEnd;
9032 
9033   PetscFunctionBegin;
9034   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9035   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9036   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
9037   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
9038   PetscFunctionReturn(PETSC_SUCCESS);
9039 }
9040 
9041 /*@
9042   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
9043 
9044   Input Parameter:
9045 . dm - The `DMPLEX` object
9046 
9047   Output Parameter:
9048 . globalCellNumbers - Global cell numbers for all cells on this process
9049 
9050   Level: developer
9051 
9052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
9053 @*/
9054 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
9055 {
9056   DM_Plex *mesh = (DM_Plex *)dm->data;
9057 
9058   PetscFunctionBegin;
9059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9060   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
9061   *globalCellNumbers = mesh->globalCellNumbers;
9062   PetscFunctionReturn(PETSC_SUCCESS);
9063 }
9064 
9065 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
9066 {
9067   PetscInt vStart, vEnd;
9068 
9069   PetscFunctionBegin;
9070   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9071   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9072   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
9073   PetscFunctionReturn(PETSC_SUCCESS);
9074 }
9075 
9076 /*@
9077   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
9078 
9079   Input Parameter:
9080 . dm - The `DMPLEX` object
9081 
9082   Output Parameter:
9083 . globalVertexNumbers - Global vertex numbers for all vertices on this process
9084 
9085   Level: developer
9086 
9087 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9088 @*/
9089 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
9090 {
9091   DM_Plex *mesh = (DM_Plex *)dm->data;
9092 
9093   PetscFunctionBegin;
9094   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9095   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
9096   *globalVertexNumbers = mesh->globalVertexNumbers;
9097   PetscFunctionReturn(PETSC_SUCCESS);
9098 }
9099 
9100 /*@
9101   DMPlexCreatePointNumbering - Create a global numbering for all points.
9102 
9103   Collective
9104 
9105   Input Parameter:
9106 . dm - The `DMPLEX` object
9107 
9108   Output Parameter:
9109 . globalPointNumbers - Global numbers for all points on this process
9110 
9111   Level: developer
9112 
9113   Notes:
9114   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
9115   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
9116   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
9117   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
9118 
9119   The partitioned mesh is
9120   ```
9121   (2)--0--(3)--1--(4)    (1)--0--(2)
9122   ```
9123   and its global numbering is
9124   ```
9125   (3)--0--(4)--1--(5)--2--(6)
9126   ```
9127   Then the global numbering is provided as
9128   ```
9129   [0] Number of indices in set 5
9130   [0] 0 0
9131   [0] 1 1
9132   [0] 2 3
9133   [0] 3 4
9134   [0] 4 -6
9135   [1] Number of indices in set 3
9136   [1] 0 2
9137   [1] 1 5
9138   [1] 2 6
9139   ```
9140 
9141 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9142 @*/
9143 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9144 {
9145   IS        nums[4];
9146   PetscInt  depths[4], gdepths[4], starts[4];
9147   PetscInt  depth, d, shift = 0;
9148   PetscBool empty = PETSC_FALSE;
9149 
9150   PetscFunctionBegin;
9151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9152   PetscCall(DMPlexGetDepth(dm, &depth));
9153   // For unstratified meshes use dim instead of depth
9154   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9155   // If any stratum is empty, we must mark all empty
9156   for (d = 0; d <= depth; ++d) {
9157     PetscInt end;
9158 
9159     depths[d] = depth - d;
9160     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9161     if (!(starts[d] - end)) empty = PETSC_TRUE;
9162   }
9163   if (empty)
9164     for (d = 0; d <= depth; ++d) {
9165       depths[d] = -1;
9166       starts[d] = -1;
9167     }
9168   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9169   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9170   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]);
9171   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9172   for (d = 0; d <= depth; ++d) {
9173     PetscInt pStart, pEnd, gsize;
9174 
9175     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9176     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9177     shift += gsize;
9178   }
9179   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9180   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9181   PetscFunctionReturn(PETSC_SUCCESS);
9182 }
9183 
9184 /*@
9185   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9186 
9187   Collective
9188 
9189   Input Parameter:
9190 . dm - The `DMPLEX` object
9191 
9192   Output Parameter:
9193 . globalEdgeNumbers - Global numbers for all edges on this process
9194 
9195   Level: developer
9196 
9197   Notes:
9198   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).
9199 
9200 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9201 @*/
9202 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9203 {
9204   PetscSF  sf;
9205   PetscInt eStart, eEnd;
9206 
9207   PetscFunctionBegin;
9208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9209   PetscCall(DMGetPointSF(dm, &sf));
9210   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9211   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9212   PetscFunctionReturn(PETSC_SUCCESS);
9213 }
9214 
9215 /*@
9216   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9217 
9218   Input Parameter:
9219 . dm - The `DMPLEX` object
9220 
9221   Output Parameter:
9222 . ranks - The rank field
9223 
9224   Options Database Key:
9225 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9226 
9227   Level: intermediate
9228 
9229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9230 @*/
9231 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9232 {
9233   DM             rdm;
9234   PetscFE        fe;
9235   PetscScalar   *r;
9236   PetscMPIInt    rank;
9237   DMPolytopeType ct;
9238   PetscInt       dim, cStart, cEnd, c;
9239   PetscBool      simplex;
9240 
9241   PetscFunctionBeginUser;
9242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9243   PetscAssertPointer(ranks, 2);
9244   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9245   PetscCall(DMClone(dm, &rdm));
9246   PetscCall(DMGetDimension(rdm, &dim));
9247   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9248   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9249   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9250   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9251   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9252   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9253   PetscCall(PetscFEDestroy(&fe));
9254   PetscCall(DMCreateDS(rdm));
9255   PetscCall(DMCreateGlobalVector(rdm, ranks));
9256   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9257   PetscCall(VecGetArray(*ranks, &r));
9258   for (c = cStart; c < cEnd; ++c) {
9259     PetscScalar *lr;
9260 
9261     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9262     if (lr) *lr = rank;
9263   }
9264   PetscCall(VecRestoreArray(*ranks, &r));
9265   PetscCall(DMDestroy(&rdm));
9266   PetscFunctionReturn(PETSC_SUCCESS);
9267 }
9268 
9269 /*@
9270   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9271 
9272   Input Parameters:
9273 + dm    - The `DMPLEX`
9274 - label - The `DMLabel`
9275 
9276   Output Parameter:
9277 . val - The label value field
9278 
9279   Options Database Key:
9280 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9281 
9282   Level: intermediate
9283 
9284 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9285 @*/
9286 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9287 {
9288   DM             rdm, plex;
9289   Vec            lval;
9290   PetscSection   section;
9291   PetscFE        fe;
9292   PetscScalar   *v;
9293   PetscInt       dim, pStart, pEnd, p, cStart;
9294   DMPolytopeType ct;
9295   char           name[PETSC_MAX_PATH_LEN];
9296   const char    *lname, *prefix;
9297 
9298   PetscFunctionBeginUser;
9299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9300   PetscAssertPointer(label, 2);
9301   PetscAssertPointer(val, 3);
9302   PetscCall(DMClone(dm, &rdm));
9303   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9304   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9305   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9306   PetscCall(DMDestroy(&plex));
9307   PetscCall(DMGetDimension(rdm, &dim));
9308   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9309   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9310   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9311   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9312   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9313   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9314   PetscCall(PetscFEDestroy(&fe));
9315   PetscCall(DMCreateDS(rdm));
9316   PetscCall(DMCreateGlobalVector(rdm, val));
9317   PetscCall(DMCreateLocalVector(rdm, &lval));
9318   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9319   PetscCall(DMGetLocalSection(rdm, &section));
9320   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9321   PetscCall(VecGetArray(lval, &v));
9322   for (p = pStart; p < pEnd; ++p) {
9323     PetscInt cval, dof, off;
9324 
9325     PetscCall(PetscSectionGetDof(section, p, &dof));
9326     if (!dof) continue;
9327     PetscCall(DMLabelGetValue(label, p, &cval));
9328     PetscCall(PetscSectionGetOffset(section, p, &off));
9329     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9330   }
9331   PetscCall(VecRestoreArray(lval, &v));
9332   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9333   PetscCall(VecDestroy(&lval));
9334   PetscCall(DMDestroy(&rdm));
9335   PetscFunctionReturn(PETSC_SUCCESS);
9336 }
9337 
9338 /*@
9339   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9340 
9341   Input Parameter:
9342 . dm - The `DMPLEX` object
9343 
9344   Level: developer
9345 
9346   Notes:
9347   This is a useful diagnostic when creating meshes programmatically.
9348 
9349   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9350 
9351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9352 @*/
9353 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9354 {
9355   PetscSection    coneSection, supportSection;
9356   const PetscInt *cone, *support;
9357   PetscInt        coneSize, c, supportSize, s;
9358   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9359   PetscBool       storagecheck = PETSC_TRUE;
9360 
9361   PetscFunctionBegin;
9362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9363   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9364   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9365   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9366   /* Check that point p is found in the support of its cone points, and vice versa */
9367   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9368   for (p = pStart; p < pEnd; ++p) {
9369     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9370     PetscCall(DMPlexGetCone(dm, p, &cone));
9371     for (c = 0; c < coneSize; ++c) {
9372       PetscBool dup = PETSC_FALSE;
9373       PetscInt  d;
9374       for (d = c - 1; d >= 0; --d) {
9375         if (cone[c] == cone[d]) {
9376           dup = PETSC_TRUE;
9377           break;
9378         }
9379       }
9380       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9381       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9382       for (s = 0; s < supportSize; ++s) {
9383         if (support[s] == p) break;
9384       }
9385       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9386         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9387         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9388         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9389         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9390         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9391         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9392         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]);
9393         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9394       }
9395     }
9396     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9397     if (p != pp) {
9398       storagecheck = PETSC_FALSE;
9399       continue;
9400     }
9401     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9402     PetscCall(DMPlexGetSupport(dm, p, &support));
9403     for (s = 0; s < supportSize; ++s) {
9404       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9405       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9406       for (c = 0; c < coneSize; ++c) {
9407         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9408         if (cone[c] != pp) {
9409           c = 0;
9410           break;
9411         }
9412         if (cone[c] == p) break;
9413       }
9414       if (c >= coneSize) {
9415         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9416         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9417         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9418         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9419         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9420         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9421         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9422       }
9423     }
9424   }
9425   if (storagecheck) {
9426     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9427     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9428     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9429   }
9430   PetscFunctionReturn(PETSC_SUCCESS);
9431 }
9432 
9433 /*
9434   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.
9435 */
9436 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9437 {
9438   DMPolytopeType  cct;
9439   PetscInt        ptpoints[4];
9440   const PetscInt *cone, *ccone, *ptcone;
9441   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9442 
9443   PetscFunctionBegin;
9444   *unsplit = 0;
9445   switch (ct) {
9446   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9447     ptpoints[npt++] = c;
9448     break;
9449   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9450     PetscCall(DMPlexGetCone(dm, c, &cone));
9451     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9452     for (cp = 0; cp < coneSize; ++cp) {
9453       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9454       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9455     }
9456     break;
9457   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9458   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9459     PetscCall(DMPlexGetCone(dm, c, &cone));
9460     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9461     for (cp = 0; cp < coneSize; ++cp) {
9462       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9463       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9464       for (ccp = 0; ccp < cconeSize; ++ccp) {
9465         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9466         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9467           PetscInt p;
9468           for (p = 0; p < npt; ++p)
9469             if (ptpoints[p] == ccone[ccp]) break;
9470           if (p == npt) ptpoints[npt++] = ccone[ccp];
9471         }
9472       }
9473     }
9474     break;
9475   default:
9476     break;
9477   }
9478   for (pt = 0; pt < npt; ++pt) {
9479     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9480     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9481   }
9482   PetscFunctionReturn(PETSC_SUCCESS);
9483 }
9484 
9485 /*@
9486   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9487 
9488   Input Parameters:
9489 + dm         - The `DMPLEX` object
9490 - cellHeight - Normally 0
9491 
9492   Level: developer
9493 
9494   Notes:
9495   This is a useful diagnostic when creating meshes programmatically.
9496   Currently applicable only to homogeneous simplex or tensor meshes.
9497 
9498   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9499 
9500 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9501 @*/
9502 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9503 {
9504   DMPlexInterpolatedFlag interp;
9505   DMPolytopeType         ct;
9506   PetscInt               vStart, vEnd, cStart, cEnd, c;
9507 
9508   PetscFunctionBegin;
9509   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9510   PetscCall(DMPlexIsInterpolated(dm, &interp));
9511   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9512   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9513   for (c = cStart; c < cEnd; ++c) {
9514     PetscInt *closure = NULL;
9515     PetscInt  coneSize, closureSize, cl, Nv = 0;
9516 
9517     PetscCall(DMPlexGetCellType(dm, c, &ct));
9518     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9519     if (interp == DMPLEX_INTERPOLATED_FULL) {
9520       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9521       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));
9522     }
9523     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9524     for (cl = 0; cl < closureSize * 2; cl += 2) {
9525       const PetscInt p = closure[cl];
9526       if ((p >= vStart) && (p < vEnd)) ++Nv;
9527     }
9528     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9529     /* Special Case: Tensor faces with identified vertices */
9530     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9531       PetscInt unsplit;
9532 
9533       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9534       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9535     }
9536     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));
9537   }
9538   PetscFunctionReturn(PETSC_SUCCESS);
9539 }
9540 
9541 /*@
9542   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9543 
9544   Collective
9545 
9546   Input Parameters:
9547 + dm         - The `DMPLEX` object
9548 - cellHeight - Normally 0
9549 
9550   Level: developer
9551 
9552   Notes:
9553   This is a useful diagnostic when creating meshes programmatically.
9554   This routine is only relevant for meshes that are fully interpolated across all ranks.
9555   It will error out if a partially interpolated mesh is given on some rank.
9556   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9557 
9558   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9559 
9560 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9561 @*/
9562 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9563 {
9564   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9565   DMPlexInterpolatedFlag interpEnum;
9566 
9567   PetscFunctionBegin;
9568   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9569   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9570   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9571   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9572     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9573     PetscFunctionReturn(PETSC_SUCCESS);
9574   }
9575 
9576   PetscCall(DMGetDimension(dm, &dim));
9577   PetscCall(DMPlexGetDepth(dm, &depth));
9578   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9579   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9580     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9581     for (c = cStart; c < cEnd; ++c) {
9582       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9583       const DMPolytopeType *faceTypes;
9584       DMPolytopeType        ct;
9585       PetscInt              numFaces, coneSize, f;
9586       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9587 
9588       PetscCall(DMPlexGetCellType(dm, c, &ct));
9589       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9590       if (unsplit) continue;
9591       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9592       PetscCall(DMPlexGetCone(dm, c, &cone));
9593       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9594       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9595       for (cl = 0; cl < closureSize * 2; cl += 2) {
9596         const PetscInt p = closure[cl];
9597         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9598       }
9599       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9600       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);
9601       for (f = 0; f < numFaces; ++f) {
9602         DMPolytopeType fct;
9603         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9604 
9605         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9606         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9607         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9608           const PetscInt p = fclosure[cl];
9609           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9610         }
9611         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]);
9612         for (v = 0; v < fnumCorners; ++v) {
9613           if (fclosure[v] != faces[fOff + v]) {
9614             PetscInt v1;
9615 
9616             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9617             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9618             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9619             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9620             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9621             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]);
9622           }
9623         }
9624         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9625         fOff += faceSizes[f];
9626       }
9627       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9628       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9629     }
9630   }
9631   PetscFunctionReturn(PETSC_SUCCESS);
9632 }
9633 
9634 /*@
9635   DMPlexCheckGeometry - Check the geometry of mesh cells
9636 
9637   Input Parameter:
9638 . dm - The `DMPLEX` object
9639 
9640   Level: developer
9641 
9642   Notes:
9643   This is a useful diagnostic when creating meshes programmatically.
9644 
9645   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9646 
9647 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9648 @*/
9649 PetscErrorCode DMPlexCheckGeometry(DM dm)
9650 {
9651   Vec       coordinates;
9652   PetscReal detJ, J[9], refVol = 1.0;
9653   PetscReal vol;
9654   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9655 
9656   PetscFunctionBegin;
9657   PetscCall(DMGetDimension(dm, &dim));
9658   PetscCall(DMGetCoordinateDim(dm, &dE));
9659   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9660   PetscCall(DMPlexGetDepth(dm, &depth));
9661   for (d = 0; d < dim; ++d) refVol *= 2.0;
9662   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9663   /* Make sure local coordinates are created, because that step is collective */
9664   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9665   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9666   for (c = cStart; c < cEnd; ++c) {
9667     DMPolytopeType ct;
9668     PetscInt       unsplit;
9669     PetscBool      ignoreZeroVol = PETSC_FALSE;
9670 
9671     PetscCall(DMPlexGetCellType(dm, c, &ct));
9672     switch (ct) {
9673     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9674     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9675     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9676       ignoreZeroVol = PETSC_TRUE;
9677       break;
9678     default:
9679       break;
9680     }
9681     switch (ct) {
9682     case DM_POLYTOPE_TRI_PRISM:
9683     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9684     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9685     case DM_POLYTOPE_PYRAMID:
9686       continue;
9687     default:
9688       break;
9689     }
9690     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9691     if (unsplit) continue;
9692     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9693     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);
9694     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9695     /* This should work with periodicity since DG coordinates should be used */
9696     if (depth > 1) {
9697       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9698       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);
9699       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9700     }
9701   }
9702   PetscFunctionReturn(PETSC_SUCCESS);
9703 }
9704 
9705 /*@
9706   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9707 
9708   Collective
9709 
9710   Input Parameters:
9711 + dm              - The `DMPLEX` object
9712 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9713 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9714 
9715   Level: developer
9716 
9717   Notes:
9718   This is mainly intended for debugging/testing purposes.
9719 
9720   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9721 
9722   Extra roots can come from periodic cuts, where additional points appear on the boundary
9723 
9724 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9725 @*/
9726 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9727 {
9728   PetscInt           l, nleaves, nroots, overlap;
9729   const PetscInt    *locals;
9730   const PetscSFNode *remotes;
9731   PetscBool          distributed;
9732   MPI_Comm           comm;
9733   PetscMPIInt        rank;
9734 
9735   PetscFunctionBegin;
9736   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9737   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9738   else pointSF = dm->sf;
9739   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9740   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9741   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9742   {
9743     PetscMPIInt mpiFlag;
9744 
9745     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9746     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9747   }
9748   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9749   PetscCall(DMPlexIsDistributed(dm, &distributed));
9750   if (!distributed) {
9751     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);
9752     PetscFunctionReturn(PETSC_SUCCESS);
9753   }
9754   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);
9755   PetscCall(DMPlexGetOverlap(dm, &overlap));
9756 
9757   /* Check SF graph is compatible with DMPlex chart */
9758   {
9759     PetscInt pStart, pEnd, maxLeaf;
9760 
9761     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9762     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9763     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9764     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9765   }
9766 
9767   /* Check there are no cells in interface */
9768   if (!overlap) {
9769     PetscInt cellHeight, cStart, cEnd;
9770 
9771     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9772     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9773     for (l = 0; l < nleaves; ++l) {
9774       const PetscInt point = locals ? locals[l] : l;
9775 
9776       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9777     }
9778   }
9779 
9780   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9781   {
9782     const PetscInt *rootdegree;
9783 
9784     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9785     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9786     for (l = 0; l < nleaves; ++l) {
9787       const PetscInt  point = locals ? locals[l] : l;
9788       const PetscInt *cone;
9789       PetscInt        coneSize, c, idx;
9790 
9791       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9792       PetscCall(DMPlexGetCone(dm, point, &cone));
9793       for (c = 0; c < coneSize; ++c) {
9794         if (!rootdegree[cone[c]]) {
9795           if (locals) {
9796             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9797           } else {
9798             idx = (cone[c] < nleaves) ? cone[c] : -1;
9799           }
9800           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9801         }
9802       }
9803     }
9804   }
9805   PetscFunctionReturn(PETSC_SUCCESS);
9806 }
9807 
9808 /*@
9809   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9810 
9811   Collective
9812 
9813   Input Parameter:
9814 . dm - The `DMPLEX` object
9815 
9816   Level: developer
9817 
9818   Notes:
9819   This is mainly intended for debugging/testing purposes.
9820 
9821   Other cell types which are disconnected would be caught by the symmetry and face checks.
9822 
9823   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9824 
9825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9826 @*/
9827 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9828 {
9829   PetscInt pStart, pEnd, vStart, vEnd;
9830 
9831   PetscFunctionBegin;
9832   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9833   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9834   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9835   for (PetscInt v = vStart; v < vEnd; ++v) {
9836     PetscInt suppSize;
9837 
9838     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9839     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9840   }
9841   PetscFunctionReturn(PETSC_SUCCESS);
9842 }
9843 
9844 /*@
9845   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9846 
9847   Input Parameter:
9848 . dm - The `DMPLEX` object
9849 
9850   Level: developer
9851 
9852   Notes:
9853   This is a useful diagnostic when creating meshes programmatically.
9854 
9855   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9856 
9857   Currently does not include `DMPlexCheckCellShape()`.
9858 
9859 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9860 @*/
9861 PetscErrorCode DMPlexCheck(DM dm)
9862 {
9863   PetscInt cellHeight;
9864 
9865   PetscFunctionBegin;
9866   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9867   PetscCall(DMPlexCheckSymmetry(dm));
9868   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9869   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9870   PetscCall(DMPlexCheckGeometry(dm));
9871   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9872   PetscCall(DMPlexCheckInterfaceCones(dm));
9873   PetscCall(DMPlexCheckOrphanVertices(dm));
9874   PetscFunctionReturn(PETSC_SUCCESS);
9875 }
9876 
9877 typedef struct cell_stats {
9878   PetscReal min, max, sum, squaresum;
9879   PetscInt  count;
9880 } cell_stats_t;
9881 
9882 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9883 {
9884   PetscInt i, N = *len;
9885 
9886   for (i = 0; i < N; i++) {
9887     cell_stats_t *A = (cell_stats_t *)a;
9888     cell_stats_t *B = (cell_stats_t *)b;
9889 
9890     B->min = PetscMin(A->min, B->min);
9891     B->max = PetscMax(A->max, B->max);
9892     B->sum += A->sum;
9893     B->squaresum += A->squaresum;
9894     B->count += A->count;
9895   }
9896 }
9897 
9898 /*@
9899   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9900 
9901   Collective
9902 
9903   Input Parameters:
9904 + dm        - The `DMPLEX` object
9905 . output    - If true, statistics will be displayed on `stdout`
9906 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9907 
9908   Level: developer
9909 
9910   Notes:
9911   This is mainly intended for debugging/testing purposes.
9912 
9913   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9914 
9915 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9916 @*/
9917 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9918 {
9919   DM           dmCoarse;
9920   cell_stats_t stats, globalStats;
9921   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9922   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9923   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9924   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9925   PetscMPIInt  rank, size;
9926 
9927   PetscFunctionBegin;
9928   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9929   stats.min = PETSC_MAX_REAL;
9930   stats.max = PETSC_MIN_REAL;
9931   stats.sum = stats.squaresum = 0.;
9932   stats.count                 = 0;
9933 
9934   PetscCallMPI(MPI_Comm_size(comm, &size));
9935   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9936   PetscCall(DMGetCoordinateDim(dm, &cdim));
9937   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9938   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9939   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9940   for (c = cStart; c < cEnd; c++) {
9941     PetscInt  i;
9942     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9943 
9944     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9945     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9946     for (i = 0; i < PetscSqr(cdim); ++i) {
9947       frobJ += J[i] * J[i];
9948       frobInvJ += invJ[i] * invJ[i];
9949     }
9950     cond2 = frobJ * frobInvJ;
9951     cond  = PetscSqrtReal(cond2);
9952 
9953     stats.min = PetscMin(stats.min, cond);
9954     stats.max = PetscMax(stats.max, cond);
9955     stats.sum += cond;
9956     stats.squaresum += cond2;
9957     stats.count++;
9958     if (output && cond > limit) {
9959       PetscSection coordSection;
9960       Vec          coordsLocal;
9961       PetscScalar *coords = NULL;
9962       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9963 
9964       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9965       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9966       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9967       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9968       for (i = 0; i < Nv / cdim; ++i) {
9969         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9970         for (d = 0; d < cdim; ++d) {
9971           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9972           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9973         }
9974         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9975       }
9976       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9977       for (cl = 0; cl < clSize * 2; cl += 2) {
9978         const PetscInt edge = closure[cl];
9979 
9980         if ((edge >= eStart) && (edge < eEnd)) {
9981           PetscReal len;
9982 
9983           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9984           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9985         }
9986       }
9987       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9988       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9989     }
9990   }
9991   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9992 
9993   if (size > 1) {
9994     PetscMPIInt  blockLengths[2] = {4, 1};
9995     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9996     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9997     MPI_Op       statReduce;
9998 
9999     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
10000     PetscCallMPI(MPI_Type_commit(&statType));
10001     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
10002     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
10003     PetscCallMPI(MPI_Op_free(&statReduce));
10004     PetscCallMPI(MPI_Type_free(&statType));
10005   } else {
10006     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
10007   }
10008   if (rank == 0) {
10009     count = globalStats.count;
10010     min   = globalStats.min;
10011     max   = globalStats.max;
10012     mean  = globalStats.sum / globalStats.count;
10013     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
10014   }
10015 
10016   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));
10017   PetscCall(PetscFree2(J, invJ));
10018 
10019   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
10020   if (dmCoarse) {
10021     PetscBool isplex;
10022 
10023     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
10024     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
10025   }
10026   PetscFunctionReturn(PETSC_SUCCESS);
10027 }
10028 
10029 /*@
10030   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
10031   orthogonal quality below given tolerance.
10032 
10033   Collective
10034 
10035   Input Parameters:
10036 + dm   - The `DMPLEX` object
10037 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
10038 - atol - [0, 1] Absolute tolerance for tagging cells.
10039 
10040   Output Parameters:
10041 + OrthQual      - `Vec` containing orthogonal quality per cell
10042 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
10043 
10044   Options Database Keys:
10045 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
10046 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
10047 
10048   Level: intermediate
10049 
10050   Notes:
10051   Orthogonal quality is given by the following formula\:
10052 
10053   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
10054 
10055   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
10056   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
10057   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
10058   calculating the cosine of the angle between these vectors.
10059 
10060   Orthogonal quality ranges from 1 (best) to 0 (worst).
10061 
10062   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
10063   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
10064 
10065   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
10066 
10067 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
10068 @*/
10069 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
10070 {
10071   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
10072   PetscInt              *idx;
10073   PetscScalar           *oqVals;
10074   const PetscScalar     *cellGeomArr, *faceGeomArr;
10075   PetscReal             *ci, *fi, *Ai;
10076   MPI_Comm               comm;
10077   Vec                    cellgeom, facegeom;
10078   DM                     dmFace, dmCell;
10079   IS                     glob;
10080   ISLocalToGlobalMapping ltog;
10081   PetscViewer            vwr;
10082 
10083   PetscFunctionBegin;
10084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10085   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
10086   PetscAssertPointer(OrthQual, 4);
10087   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
10088   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
10089   PetscCall(DMGetDimension(dm, &nc));
10090   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
10091   {
10092     DMPlexInterpolatedFlag interpFlag;
10093 
10094     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
10095     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
10096       PetscMPIInt rank;
10097 
10098       PetscCallMPI(MPI_Comm_rank(comm, &rank));
10099       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
10100     }
10101   }
10102   if (OrthQualLabel) {
10103     PetscAssertPointer(OrthQualLabel, 5);
10104     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
10105     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
10106   } else {
10107     *OrthQualLabel = NULL;
10108   }
10109   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
10110   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
10111   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
10112   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
10113   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
10114   PetscCall(VecCreate(comm, OrthQual));
10115   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
10116   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
10117   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
10118   PetscCall(VecSetUp(*OrthQual));
10119   PetscCall(ISDestroy(&glob));
10120   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
10121   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
10122   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
10123   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10124   PetscCall(VecGetDM(cellgeom, &dmCell));
10125   PetscCall(VecGetDM(facegeom, &dmFace));
10126   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10127   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10128     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10129     PetscInt         cellarr[2], *adj = NULL;
10130     PetscScalar     *cArr, *fArr;
10131     PetscReal        minvalc = 1.0, minvalf = 1.0;
10132     PetscFVCellGeom *cg;
10133 
10134     idx[cellIter] = cell - cStart;
10135     cellarr[0]    = cell;
10136     /* Make indexing into cellGeom easier */
10137     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10138     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10139     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10140     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10141     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10142       PetscInt         i;
10143       const PetscInt   neigh  = adj[cellneigh];
10144       PetscReal        normci = 0, normfi = 0, normai = 0;
10145       PetscFVCellGeom *cgneigh;
10146       PetscFVFaceGeom *fg;
10147 
10148       /* Don't count ourselves in the neighbor list */
10149       if (neigh == cell) continue;
10150       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10151       cellarr[1] = neigh;
10152       {
10153         PetscInt        numcovpts;
10154         const PetscInt *covpts;
10155 
10156         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10157         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10158         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10159       }
10160 
10161       /* Compute c_i, f_i and their norms */
10162       for (i = 0; i < nc; i++) {
10163         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10164         fi[i] = fg->centroid[i] - cg->centroid[i];
10165         Ai[i] = fg->normal[i];
10166         normci += PetscPowReal(ci[i], 2);
10167         normfi += PetscPowReal(fi[i], 2);
10168         normai += PetscPowReal(Ai[i], 2);
10169       }
10170       normci = PetscSqrtReal(normci);
10171       normfi = PetscSqrtReal(normfi);
10172       normai = PetscSqrtReal(normai);
10173 
10174       /* Normalize and compute for each face-cell-normal pair */
10175       for (i = 0; i < nc; i++) {
10176         ci[i] = ci[i] / normci;
10177         fi[i] = fi[i] / normfi;
10178         Ai[i] = Ai[i] / normai;
10179         /* PetscAbs because I don't know if normals are guaranteed to point out */
10180         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10181         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10182       }
10183       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10184       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10185     }
10186     PetscCall(PetscFree(adj));
10187     PetscCall(PetscFree2(cArr, fArr));
10188     /* Defer to cell if they're equal */
10189     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10190     if (OrthQualLabel) {
10191       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10192     }
10193   }
10194   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10195   PetscCall(VecAssemblyBegin(*OrthQual));
10196   PetscCall(VecAssemblyEnd(*OrthQual));
10197   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10198   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10199   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10200   if (OrthQualLabel) {
10201     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10202   }
10203   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10204   PetscCall(PetscViewerDestroy(&vwr));
10205   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10206   PetscFunctionReturn(PETSC_SUCCESS);
10207 }
10208 
10209 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10210  * interpolator construction */
10211 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10212 {
10213   PetscSection section, newSection, gsection;
10214   PetscSF      sf;
10215   PetscBool    hasConstraints, ghasConstraints;
10216 
10217   PetscFunctionBegin;
10218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10219   PetscAssertPointer(odm, 2);
10220   PetscCall(DMGetLocalSection(dm, &section));
10221   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10222   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10223   if (!ghasConstraints) {
10224     PetscCall(PetscObjectReference((PetscObject)dm));
10225     *odm = dm;
10226     PetscFunctionReturn(PETSC_SUCCESS);
10227   }
10228   PetscCall(DMClone(dm, odm));
10229   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10230   PetscCall(DMGetLocalSection(*odm, &newSection));
10231   PetscCall(DMGetPointSF(*odm, &sf));
10232   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10233   PetscCall(DMSetGlobalSection(*odm, gsection));
10234   PetscCall(PetscSectionDestroy(&gsection));
10235   PetscFunctionReturn(PETSC_SUCCESS);
10236 }
10237 
10238 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10239 {
10240   DM        dmco, dmfo;
10241   Mat       interpo;
10242   Vec       rscale;
10243   Vec       cglobalo, clocal;
10244   Vec       fglobal, fglobalo, flocal;
10245   PetscBool regular;
10246 
10247   PetscFunctionBegin;
10248   PetscCall(DMGetFullDM(dmc, &dmco));
10249   PetscCall(DMGetFullDM(dmf, &dmfo));
10250   PetscCall(DMSetCoarseDM(dmfo, dmco));
10251   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10252   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10253   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10254   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10255   PetscCall(DMCreateLocalVector(dmc, &clocal));
10256   PetscCall(VecSet(cglobalo, 0.));
10257   PetscCall(VecSet(clocal, 0.));
10258   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10259   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10260   PetscCall(DMCreateLocalVector(dmf, &flocal));
10261   PetscCall(VecSet(fglobal, 0.));
10262   PetscCall(VecSet(fglobalo, 0.));
10263   PetscCall(VecSet(flocal, 0.));
10264   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10265   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10266   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10267   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10268   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10269   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10270   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10271   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10272   *shift = fglobal;
10273   PetscCall(VecDestroy(&flocal));
10274   PetscCall(VecDestroy(&fglobalo));
10275   PetscCall(VecDestroy(&clocal));
10276   PetscCall(VecDestroy(&cglobalo));
10277   PetscCall(VecDestroy(&rscale));
10278   PetscCall(MatDestroy(&interpo));
10279   PetscCall(DMDestroy(&dmfo));
10280   PetscCall(DMDestroy(&dmco));
10281   PetscFunctionReturn(PETSC_SUCCESS);
10282 }
10283 
10284 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10285 {
10286   PetscObject shifto;
10287   Vec         shift;
10288 
10289   PetscFunctionBegin;
10290   if (!interp) {
10291     Vec rscale;
10292 
10293     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10294     PetscCall(VecDestroy(&rscale));
10295   } else {
10296     PetscCall(PetscObjectReference((PetscObject)interp));
10297   }
10298   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10299   if (!shifto) {
10300     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10301     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10302     shifto = (PetscObject)shift;
10303     PetscCall(VecDestroy(&shift));
10304   }
10305   shift = (Vec)shifto;
10306   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10307   PetscCall(VecAXPY(fineSol, 1.0, shift));
10308   PetscCall(MatDestroy(&interp));
10309   PetscFunctionReturn(PETSC_SUCCESS);
10310 }
10311 
10312 /* Pointwise interpolation
10313      Just code FEM for now
10314      u^f = I u^c
10315      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10316      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10317      I_{ij} = psi^f_i phi^c_j
10318 */
10319 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10320 {
10321   PetscSection gsc, gsf;
10322   PetscInt     m, n;
10323   void        *ctx;
10324   DM           cdm;
10325   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10326 
10327   PetscFunctionBegin;
10328   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10329   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10330   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10331   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10332 
10333   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10334   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10335   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10336   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10337   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10338 
10339   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10340   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10341   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10342   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10343   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10344   if (scaling) {
10345     /* Use naive scaling */
10346     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10347   }
10348   PetscFunctionReturn(PETSC_SUCCESS);
10349 }
10350 
10351 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10352 {
10353   VecScatter ctx;
10354 
10355   PetscFunctionBegin;
10356   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10357   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10358   PetscCall(VecScatterDestroy(&ctx));
10359   PetscFunctionReturn(PETSC_SUCCESS);
10360 }
10361 
10362 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[])
10363 {
10364   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10365   const PetscInt Nc = uOff[f + 1] - uOff[f];
10366   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10367 }
10368 
10369 // The assumption here is that the test field is a vector and the basis field is a scalar (so we need the gradient)
10370 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[])
10371 {
10372   for (PetscInt c = 0; c < dim; ++c) g1[c * dim + c] = 1.0;
10373 }
10374 
10375 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10376 {
10377   DM           dmc;
10378   PetscDS      ds;
10379   Vec          ones, locmass;
10380   IS           cellIS;
10381   PetscFormKey key;
10382   PetscInt     depth;
10383 
10384   PetscFunctionBegin;
10385   PetscCall(DMClone(dm, &dmc));
10386   PetscCall(DMCopyDisc(dm, dmc));
10387   PetscCall(DMGetDS(dmc, &ds));
10388   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10389   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10390   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10391   else PetscCall(DMGetLocalVector(dm, &locmass));
10392   PetscCall(DMGetLocalVector(dm, &ones));
10393   PetscCall(DMPlexGetDepth(dm, &depth));
10394   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10395   PetscCall(VecSet(locmass, 0.0));
10396   PetscCall(VecSet(ones, 1.0));
10397   key.label = NULL;
10398   key.value = 0;
10399   key.field = 0;
10400   key.part  = 0;
10401   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10402   PetscCall(ISDestroy(&cellIS));
10403   if (mass) {
10404     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10405     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10406   }
10407   PetscCall(DMRestoreLocalVector(dm, &ones));
10408   if (lmass) *lmass = locmass;
10409   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10410   PetscCall(DMDestroy(&dmc));
10411   PetscFunctionReturn(PETSC_SUCCESS);
10412 }
10413 
10414 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10415 {
10416   PetscSection gsc, gsf;
10417   PetscInt     m, n;
10418   void        *ctx;
10419   DM           cdm;
10420   PetscBool    regular;
10421 
10422   PetscFunctionBegin;
10423   if (dmFine == dmCoarse) {
10424     DM            dmc;
10425     PetscDS       ds;
10426     PetscWeakForm wf;
10427     Vec           u;
10428     IS            cellIS;
10429     PetscFormKey  key;
10430     PetscInt      depth;
10431 
10432     PetscCall(DMClone(dmFine, &dmc));
10433     PetscCall(DMCopyDisc(dmFine, dmc));
10434     PetscCall(DMGetDS(dmc, &ds));
10435     PetscCall(PetscDSGetWeakForm(ds, &wf));
10436     PetscCall(PetscWeakFormClear(wf));
10437     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10438     PetscCall(DMCreateMatrix(dmc, mass));
10439     PetscCall(DMGetLocalVector(dmc, &u));
10440     PetscCall(DMPlexGetDepth(dmc, &depth));
10441     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10442     PetscCall(MatZeroEntries(*mass));
10443     key.label = NULL;
10444     key.value = 0;
10445     key.field = 0;
10446     key.part  = 0;
10447     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10448     PetscCall(ISDestroy(&cellIS));
10449     PetscCall(DMRestoreLocalVector(dmc, &u));
10450     PetscCall(DMDestroy(&dmc));
10451   } else {
10452     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10453     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10454     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10455     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10456 
10457     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10458     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10459     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10460     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10461 
10462     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10463     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10464     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10465     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10466   }
10467   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10468   PetscFunctionReturn(PETSC_SUCCESS);
10469 }
10470 
10471 PetscErrorCode DMCreateGradientMatrix_Plex(DM dmc, DM dmr, Mat *derv)
10472 {
10473   PetscSection gsc, gsf;
10474   PetscInt     m, n;
10475   void        *ctx;
10476 
10477   PetscFunctionBegin;
10478   PetscCall(DMGetGlobalSection(dmr, &gsf));
10479   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10480   PetscCall(DMGetGlobalSection(dmc, &gsc));
10481   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10482 
10483   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmc), derv));
10484   PetscCall(PetscObjectSetName((PetscObject)*derv, "Plex Derivative Matrix"));
10485   PetscCall(MatSetSizes(*derv, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10486   PetscCall(MatSetType(*derv, dmc->mattype));
10487 
10488   PetscCall(DMGetApplicationContext(dmr, &ctx));
10489   {
10490     DM            ndmr;
10491     PetscDS       ds;
10492     PetscWeakForm wf;
10493     Vec           u;
10494     IS            cellIS;
10495     PetscFormKey  key;
10496     PetscInt      depth, Nf;
10497 
10498     PetscCall(DMClone(dmr, &ndmr));
10499     PetscCall(DMCopyDisc(dmr, ndmr));
10500     PetscCall(DMGetDS(ndmr, &ds));
10501     PetscCall(PetscDSGetWeakForm(ds, &wf));
10502     PetscCall(PetscWeakFormClear(wf));
10503     PetscCall(PetscDSGetNumFields(ds, &Nf));
10504     for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, NULL, g1_identity_private, NULL, NULL));
10505     PetscCall(DMGetLocalVector(ndmr, &u));
10506     PetscCall(DMPlexGetDepth(ndmr, &depth));
10507     PetscCall(DMGetStratumIS(ndmr, "depth", depth, &cellIS));
10508     PetscCall(MatZeroEntries(*derv));
10509     key.label = NULL;
10510     key.value = 0;
10511     key.field = 0;
10512     key.part  = 0;
10513     PetscCall(DMPlexComputeJacobianByKeyGeneral(ndmr, dmc, key, cellIS, 0.0, 0.0, u, NULL, *derv, *derv, NULL));
10514     PetscCall(ISDestroy(&cellIS));
10515     PetscCall(DMRestoreLocalVector(ndmr, &u));
10516     PetscCall(DMDestroy(&ndmr));
10517   }
10518   PetscCall(MatViewFromOptions(*derv, NULL, "-gradient_mat_view"));
10519   PetscFunctionReturn(PETSC_SUCCESS);
10520 }
10521 
10522 /*@
10523   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10524 
10525   Input Parameter:
10526 . dm - The `DMPLEX` object
10527 
10528   Output Parameter:
10529 . regular - The flag
10530 
10531   Level: intermediate
10532 
10533 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10534 @*/
10535 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10536 {
10537   PetscFunctionBegin;
10538   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10539   PetscAssertPointer(regular, 2);
10540   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10541   PetscFunctionReturn(PETSC_SUCCESS);
10542 }
10543 
10544 /*@
10545   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10546 
10547   Input Parameters:
10548 + dm      - The `DMPLEX` object
10549 - regular - The flag
10550 
10551   Level: intermediate
10552 
10553 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10554 @*/
10555 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10556 {
10557   PetscFunctionBegin;
10558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10559   ((DM_Plex *)dm->data)->regularRefinement = regular;
10560   PetscFunctionReturn(PETSC_SUCCESS);
10561 }
10562 
10563 /*@
10564   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10565   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10566 
10567   Not Collective
10568 
10569   Input Parameter:
10570 . dm - The `DMPLEX` object
10571 
10572   Output Parameters:
10573 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10574 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10575 
10576   Level: intermediate
10577 
10578 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10579 @*/
10580 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10581 {
10582   DM_Plex *plex = (DM_Plex *)dm->data;
10583 
10584   PetscFunctionBegin;
10585   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10586   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10587   if (anchorSection) *anchorSection = plex->anchorSection;
10588   if (anchorIS) *anchorIS = plex->anchorIS;
10589   PetscFunctionReturn(PETSC_SUCCESS);
10590 }
10591 
10592 /*@
10593   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10594 
10595   Collective
10596 
10597   Input Parameters:
10598 + dm            - The `DMPLEX` object
10599 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10600                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10601 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10602 
10603   Level: intermediate
10604 
10605   Notes:
10606   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10607   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10608   combination of other points' degrees of freedom.
10609 
10610   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10611   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10612 
10613   The reference counts of `anchorSection` and `anchorIS` are incremented.
10614 
10615 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10616 @*/
10617 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10618 {
10619   DM_Plex    *plex = (DM_Plex *)dm->data;
10620   PetscMPIInt result;
10621 
10622   PetscFunctionBegin;
10623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10624   if (anchorSection) {
10625     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10626     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10627     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10628   }
10629   if (anchorIS) {
10630     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10631     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10632     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10633   }
10634 
10635   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10636   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10637   plex->anchorSection = anchorSection;
10638 
10639   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10640   PetscCall(ISDestroy(&plex->anchorIS));
10641   plex->anchorIS = anchorIS;
10642 
10643   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10644     PetscInt        size, a, pStart, pEnd;
10645     const PetscInt *anchors;
10646 
10647     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10648     PetscCall(ISGetLocalSize(anchorIS, &size));
10649     PetscCall(ISGetIndices(anchorIS, &anchors));
10650     for (a = 0; a < size; a++) {
10651       PetscInt p;
10652 
10653       p = anchors[a];
10654       if (p >= pStart && p < pEnd) {
10655         PetscInt dof;
10656 
10657         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10658         if (dof) {
10659           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10660           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10661         }
10662       }
10663     }
10664     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10665   }
10666   /* reset the generic constraints */
10667   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10668   PetscFunctionReturn(PETSC_SUCCESS);
10669 }
10670 
10671 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10672 {
10673   PetscSection anchorSection;
10674   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10675 
10676   PetscFunctionBegin;
10677   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10678   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10679   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10680   PetscCall(PetscSectionGetNumFields(section, &numFields));
10681   if (numFields) {
10682     PetscInt f;
10683     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10684 
10685     for (f = 0; f < numFields; f++) {
10686       PetscInt numComp;
10687 
10688       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10689       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10690     }
10691   }
10692   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10693   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10694   pStart = PetscMax(pStart, sStart);
10695   pEnd   = PetscMin(pEnd, sEnd);
10696   pEnd   = PetscMax(pStart, pEnd);
10697   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10698   for (p = pStart; p < pEnd; p++) {
10699     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10700     if (dof) {
10701       PetscCall(PetscSectionGetDof(section, p, &dof));
10702       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10703       for (f = 0; f < numFields; f++) {
10704         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10705         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10706       }
10707     }
10708   }
10709   PetscCall(PetscSectionSetUp(*cSec));
10710   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10711   PetscFunctionReturn(PETSC_SUCCESS);
10712 }
10713 
10714 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10715 {
10716   PetscSection    aSec;
10717   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10718   const PetscInt *anchors;
10719   PetscInt        numFields, f;
10720   IS              aIS;
10721   MatType         mtype;
10722   PetscBool       iscuda, iskokkos;
10723 
10724   PetscFunctionBegin;
10725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10726   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10727   PetscCall(PetscSectionGetStorageSize(section, &n));
10728   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10729   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10730   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10731   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10732   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10733   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10734   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10735   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10736   else mtype = MATSEQAIJ;
10737   PetscCall(MatSetType(*cMat, mtype));
10738   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10739   PetscCall(ISGetIndices(aIS, &anchors));
10740   /* cSec will be a subset of aSec and section */
10741   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10742   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10743   PetscCall(PetscMalloc1(m + 1, &i));
10744   i[0] = 0;
10745   PetscCall(PetscSectionGetNumFields(section, &numFields));
10746   for (p = pStart; p < pEnd; p++) {
10747     PetscInt rDof, rOff, r;
10748 
10749     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10750     if (!rDof) continue;
10751     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10752     if (numFields) {
10753       for (f = 0; f < numFields; f++) {
10754         annz = 0;
10755         for (r = 0; r < rDof; r++) {
10756           a = anchors[rOff + r];
10757           if (a < sStart || a >= sEnd) continue;
10758           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10759           annz += aDof;
10760         }
10761         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10762         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10763         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10764       }
10765     } else {
10766       annz = 0;
10767       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10768       for (q = 0; q < dof; q++) {
10769         a = anchors[rOff + q];
10770         if (a < sStart || a >= sEnd) continue;
10771         PetscCall(PetscSectionGetDof(section, a, &aDof));
10772         annz += aDof;
10773       }
10774       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10775       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10776       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10777     }
10778   }
10779   nnz = i[m];
10780   PetscCall(PetscMalloc1(nnz, &j));
10781   offset = 0;
10782   for (p = pStart; p < pEnd; p++) {
10783     if (numFields) {
10784       for (f = 0; f < numFields; f++) {
10785         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10786         for (q = 0; q < dof; q++) {
10787           PetscInt rDof, rOff, r;
10788           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10789           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10790           for (r = 0; r < rDof; r++) {
10791             PetscInt s;
10792 
10793             a = anchors[rOff + r];
10794             if (a < sStart || a >= sEnd) continue;
10795             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10796             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10797             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10798           }
10799         }
10800       }
10801     } else {
10802       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10803       for (q = 0; q < dof; q++) {
10804         PetscInt rDof, rOff, r;
10805         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10806         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10807         for (r = 0; r < rDof; r++) {
10808           PetscInt s;
10809 
10810           a = anchors[rOff + r];
10811           if (a < sStart || a >= sEnd) continue;
10812           PetscCall(PetscSectionGetDof(section, a, &aDof));
10813           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10814           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10815         }
10816       }
10817     }
10818   }
10819   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10820   PetscCall(PetscFree(i));
10821   PetscCall(PetscFree(j));
10822   PetscCall(ISRestoreIndices(aIS, &anchors));
10823   PetscFunctionReturn(PETSC_SUCCESS);
10824 }
10825 
10826 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10827 {
10828   DM_Plex     *plex = (DM_Plex *)dm->data;
10829   PetscSection anchorSection, section, cSec;
10830   Mat          cMat;
10831 
10832   PetscFunctionBegin;
10833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10834   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10835   if (anchorSection) {
10836     PetscInt Nf;
10837 
10838     PetscCall(DMGetLocalSection(dm, &section));
10839     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10840     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10841     PetscCall(DMGetNumFields(dm, &Nf));
10842     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10843     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10844     PetscCall(PetscSectionDestroy(&cSec));
10845     PetscCall(MatDestroy(&cMat));
10846   }
10847   PetscFunctionReturn(PETSC_SUCCESS);
10848 }
10849 
10850 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10851 {
10852   IS           subis;
10853   PetscSection section, subsection;
10854 
10855   PetscFunctionBegin;
10856   PetscCall(DMGetLocalSection(dm, &section));
10857   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10858   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10859   /* Create subdomain */
10860   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, PetscObjectComm((PetscObject)dm), NULL, subdm));
10861   /* Create submodel */
10862   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10863   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10864   PetscCall(DMSetLocalSection(*subdm, subsection));
10865   PetscCall(PetscSectionDestroy(&subsection));
10866   PetscCall(DMCopyDisc(dm, *subdm));
10867   /* Create map from submodel to global model */
10868   if (is) {
10869     PetscSection    sectionGlobal, subsectionGlobal;
10870     IS              spIS;
10871     const PetscInt *spmap;
10872     PetscInt       *subIndices;
10873     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10874     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10875 
10876     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10877     PetscCall(ISGetIndices(spIS, &spmap));
10878     PetscCall(PetscSectionGetNumFields(section, &Nf));
10879     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10880     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10881     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10882     for (p = pStart; p < pEnd; ++p) {
10883       PetscInt gdof, pSubSize = 0;
10884 
10885       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10886       if (gdof > 0) {
10887         for (f = 0; f < Nf; ++f) {
10888           PetscInt fdof, fcdof;
10889 
10890           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10891           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10892           pSubSize += fdof - fcdof;
10893         }
10894         subSize += pSubSize;
10895         if (pSubSize) {
10896           if (bs < 0) {
10897             bs = pSubSize;
10898           } else if (bs != pSubSize) {
10899             /* Layout does not admit a pointwise block size */
10900             bs = 1;
10901           }
10902         }
10903       }
10904     }
10905     /* Must have same blocksize on all procs (some might have no points) */
10906     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10907     bsLocal[1] = bs;
10908     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10909     if (bsMinMax[0] != bsMinMax[1]) {
10910       bs = 1;
10911     } else {
10912       bs = bsMinMax[0];
10913     }
10914     PetscCall(PetscMalloc1(subSize, &subIndices));
10915     for (p = pStart; p < pEnd; ++p) {
10916       PetscInt gdof, goff;
10917 
10918       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10919       if (gdof > 0) {
10920         const PetscInt point = spmap[p];
10921 
10922         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10923         for (f = 0; f < Nf; ++f) {
10924           PetscInt fdof, fcdof, fc, f2, poff = 0;
10925 
10926           /* Can get rid of this loop by storing field information in the global section */
10927           for (f2 = 0; f2 < f; ++f2) {
10928             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10929             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10930             poff += fdof - fcdof;
10931           }
10932           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10933           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10934           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10935         }
10936       }
10937     }
10938     PetscCall(ISRestoreIndices(spIS, &spmap));
10939     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10940     if (bs > 1) {
10941       /* We need to check that the block size does not come from non-contiguous fields */
10942       PetscInt i, j, set = 1;
10943       for (i = 0; i < subSize; i += bs) {
10944         for (j = 0; j < bs; ++j) {
10945           if (subIndices[i + j] != subIndices[i] + j) {
10946             set = 0;
10947             break;
10948           }
10949         }
10950       }
10951       if (set) PetscCall(ISSetBlockSize(*is, bs));
10952     }
10953     // Attach nullspace
10954     if (dm->nullspaceConstructors) {
10955       for (f = 0; f < Nf; ++f) {
10956         (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10957         if ((*subdm)->nullspaceConstructors[f]) break;
10958       }
10959       if (f < Nf) {
10960         MatNullSpace nullSpace;
10961         PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10962 
10963         PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10964         PetscCall(MatNullSpaceDestroy(&nullSpace));
10965       }
10966     }
10967   }
10968   PetscFunctionReturn(PETSC_SUCCESS);
10969 }
10970 
10971 /*@
10972   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10973 
10974   Input Parameters:
10975 + dm     - The `DM`
10976 - unused - unused argument
10977 
10978   Options Database Key:
10979 . -dm_plex_monitor_throughput - Activate the monitor
10980 
10981   Level: developer
10982 
10983 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10984 @*/
10985 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *unused)
10986 {
10987   PetscLogHandler default_handler;
10988 
10989   PetscFunctionBegin;
10990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10991   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10992   if (default_handler) {
10993     PetscLogEvent      event;
10994     PetscEventPerfInfo eventInfo;
10995     PetscLogDouble     cellRate, flopRate;
10996     PetscInt           cStart, cEnd, Nf, N;
10997     const char        *name;
10998 
10999     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
11000     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
11001     PetscCall(DMGetNumFields(dm, &Nf));
11002     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
11003     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
11004     N        = (cEnd - cStart) * Nf * eventInfo.count;
11005     flopRate = eventInfo.flops / eventInfo.time;
11006     cellRate = N / eventInfo.time;
11007     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));
11008   } else {
11009     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.");
11010   }
11011   PetscFunctionReturn(PETSC_SUCCESS);
11012 }
11013