xref: /petsc/src/dm/impls/plex/plex.c (revision caff39ffb1e873954b735c9080d09d6ce1bd0978)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_CreateBoxSFC, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 /* Logging support */
18 PetscLogEvent DMPLEX_DistributionView, DMPLEX_DistributionLoad;
19 
20 PetscBool  Plexcite       = PETSC_FALSE;
21 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
22                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
23                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
24                             "journal   = {SIAM Journal on Scientific Computing},\n"
25                             "volume    = {38},\n"
26                             "number    = {5},\n"
27                             "pages     = {S143--S155},\n"
28                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
29                             "doi       = {10.1137/15M1026092},\n"
30                             "year      = {2016},\n"
31                             "petsc_uses={DMPlex},\n}\n";
32 
33 PETSC_SINGLE_LIBRARY_INTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
34 
35 /*@
36   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
37 
38   Input Parameter:
39 . dm - The `DMPLEX` object
40 
41   Output Parameter:
42 . simplex - Flag checking for a simplex
43 
44   Level: intermediate
45 
46   Note:
47   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
48   If the mesh has no cells, this returns `PETSC_FALSE`.
49 
50 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
51 @*/
52 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
53 {
54   DMPolytopeType ct;
55   PetscInt       cStart, cEnd;
56 
57   PetscFunctionBegin;
58   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
59   if (cEnd <= cStart) {
60     *simplex = PETSC_FALSE;
61     PetscFunctionReturn(PETSC_SUCCESS);
62   }
63   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
64   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@
69   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
70 
71   Input Parameters:
72 + dm     - The `DMPLEX` object
73 - height - The cell height in the Plex, 0 is the default
74 
75   Output Parameters:
76 + cStart - The first "normal" cell, pass `NULL` if not needed
77 - cEnd   - The upper bound on "normal" cells, pass `NULL` if not needed
78 
79   Level: developer
80 
81   Note:
82   This function requires that tensor cells are ordered last.
83 
84 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
85 @*/
86 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PeOp PetscInt *cStart, PeOp PetscInt *cEnd)
87 {
88   DMLabel         ctLabel;
89   IS              valueIS;
90   const PetscInt *ctypes;
91   PetscBool       found = PETSC_FALSE;
92   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
93 
94   PetscFunctionBegin;
95   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
96   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
97   PetscCall(ISGetLocalSize(valueIS, &Nct));
98   PetscCall(ISGetIndices(valueIS, &ctypes));
99   for (PetscInt t = 0; t < Nct; ++t) {
100     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
101     PetscInt             ctS, ctE, ht;
102 
103     if (ct == DM_POLYTOPE_UNKNOWN) {
104       // If any cells are not typed, just use all cells
105       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
106       break;
107     }
108     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
109     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
110     if (ctS >= ctE) continue;
111     // Check that a point has the right height
112     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
113     if (ht != height) continue;
114     cS    = PetscMin(cS, ctS);
115     cE    = PetscMax(cE, ctE);
116     found = PETSC_TRUE;
117   }
118   if (!Nct || !found) cS = cE = 0;
119   PetscCall(ISDestroy(&valueIS));
120   // Reset label for fast lookup
121   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
122   if (cStart) *cStart = cS;
123   if (cEnd) *cEnd = cE;
124   PetscFunctionReturn(PETSC_SUCCESS);
125 }
126 
127 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
128 {
129   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
130   PetscInt                *sStart, *sEnd;
131   PetscViewerVTKFieldType *ft;
132   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
133   DMLabel                  depthLabel, ctLabel;
134 
135   PetscFunctionBegin;
136   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
137   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
138   PetscCall(DMGetCoordinateDim(dm, &cdim));
139   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
140   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
141   if (field >= 0) {
142     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
143   } else {
144     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
145   }
146 
147   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
148   PetscCall(DMPlexGetDepth(dm, &depth));
149   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
150   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
151   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
152     const DMPolytopeType ict = (DMPolytopeType)c;
153     PetscInt             dep;
154 
155     if (ict == DM_POLYTOPE_FV_GHOST) continue;
156     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
157     if (pStart >= 0) {
158       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
159       if (dep != depth - cellHeight) continue;
160     }
161     if (field >= 0) {
162       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
163     } else {
164       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
165     }
166   }
167 
168   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
169   *types = 0;
170 
171   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
172     if (globalvcdof[c]) ++(*types);
173   }
174 
175   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
176   t = 0;
177   if (globalvcdof[DM_NUM_POLYTOPES]) {
178     sStart[t] = vStart;
179     sEnd[t]   = vEnd;
180     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
181     ++t;
182   }
183 
184   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
185     if (globalvcdof[c]) {
186       const DMPolytopeType ict = (DMPolytopeType)c;
187 
188       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
189       sStart[t] = cStart;
190       sEnd[t]   = cEnd;
191       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
192       ++t;
193     }
194   }
195 
196   if (!*types) {
197     if (field >= 0) {
198       const char *fieldname;
199 
200       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
201       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
202     } else {
203       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
204     }
205   }
206 
207   *ssStart = sStart;
208   *ssEnd   = sEnd;
209   *sft     = ft;
210   PetscFunctionReturn(PETSC_SUCCESS);
211 }
212 
213 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
214 {
215   PetscFunctionBegin;
216   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
221 {
222   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
223   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
224 
225   PetscFunctionBegin;
226   *ft = PETSC_VTK_INVALID;
227   PetscCall(DMGetCoordinateDim(dm, &cdim));
228   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
229   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
230   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
231   if (field >= 0) {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
234   } else {
235     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
236     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
237   }
238   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
239   if (globalvcdof[0]) {
240     *sStart = vStart;
241     *sEnd   = vEnd;
242     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
243     else *ft = PETSC_VTK_POINT_FIELD;
244   } else if (globalvcdof[1]) {
245     *sStart = cStart;
246     *sEnd   = cEnd;
247     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
248     else *ft = PETSC_VTK_CELL_FIELD;
249   } else {
250     if (field >= 0) {
251       const char *fieldname;
252 
253       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
254       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
255     } else {
256       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section\n"));
257     }
258   }
259   PetscFunctionReturn(PETSC_SUCCESS);
260 }
261 
262 /*@
263   DMPlexVecView1D - Plot many 1D solutions on the same line graph
264 
265   Collective
266 
267   Input Parameters:
268 + dm     - The `DMPLEX` object
269 . n      - The number of vectors
270 . u      - The array of local vectors
271 - viewer - The `PetscViewer`
272 
273   Level: advanced
274 
275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
276 @*/
277 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
278 {
279   DM                 cdm;
280   PetscDS            ds;
281   PetscDraw          draw = NULL;
282   PetscDrawLG        lg;
283   Vec                coordinates;
284   const PetscScalar *coords, **sol;
285   PetscReal         *vals;
286   PetscInt          *Nc;
287   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
288   char             **names;
289 
290   PetscFunctionBegin;
291   PetscCall(DMGetCoordinateDM(dm, &cdm));
292   PetscCall(DMGetDS(dm, &ds));
293   PetscCall(PetscDSGetNumFields(ds, &Nf));
294   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
295   PetscCall(PetscDSGetComponents(ds, &Nc));
296 
297   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
298   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
299   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
300 
301   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
302   for (PetscInt i = 0, l = 0; i < n; ++i) {
303     const char *vname;
304 
305     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
306     for (PetscInt f = 0; f < Nf; ++f) {
307       PetscObject disc;
308       const char *fname;
309       char        tmpname[PETSC_MAX_PATH_LEN];
310 
311       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
312       /* TODO Create names for components */
313       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
314         PetscCall(PetscObjectGetName(disc, &fname));
315         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
316         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
317         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
318         PetscCall(PetscStrallocpy(tmpname, &names[l]));
319       }
320     }
321   }
322   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
323   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
324   PetscCall(VecGetArrayRead(coordinates, &coords));
325   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
326   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
327   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
328   PetscSection s;
329   PetscInt     cdof, vdof;
330 
331   PetscCall(DMGetLocalSection(dm, &s));
332   PetscCall(PetscSectionGetDof(s, eStart, &cdof));
333   PetscCall(PetscSectionGetDof(s, vStart, &vdof));
334   if (cdof) {
335     if (vdof) {
336       // P_2
337       PetscInt vFirst = -1;
338 
339       for (PetscInt e = eStart; e < eEnd; ++e) {
340         PetscScalar    *xa, *xb, *svals;
341         const PetscInt *cone;
342 
343         PetscCall(DMPlexGetCone(dm, e, &cone));
344         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
345         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
346         if (e == eStart) vFirst = cone[0];
347         for (PetscInt i = 0; i < n; ++i) {
348           PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
349           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
350         }
351         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
352         if (e == eEnd - 1 && cone[1] != vFirst) {
353           for (PetscInt i = 0; i < n; ++i) {
354             PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
355             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
356           }
357           PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
358           for (PetscInt i = 0; i < n; ++i) {
359             PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
360             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
361           }
362           PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
363         }
364       }
365     } else {
366       // P_0
367       for (PetscInt e = eStart; e < eEnd; ++e) {
368         PetscScalar    *xa, *xb, *svals;
369         const PetscInt *cone;
370 
371         PetscCall(DMPlexGetCone(dm, e, &cone));
372         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
373         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
374         for (PetscInt i = 0; i < n; ++i) {
375           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
376           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
377         }
378         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
379       }
380     }
381   } else if (vdof) {
382     // P_1
383     for (PetscInt v = vStart; v < vEnd; ++v) {
384       PetscScalar *x, *svals;
385 
386       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
387       for (PetscInt i = 0; i < n; ++i) {
388         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
389         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
390       }
391       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
392     }
393   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Discretization not supported");
394   PetscCall(VecRestoreArrayRead(coordinates, &coords));
395   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
396   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
397   PetscCall(PetscFree3(sol, names, vals));
398 
399   PetscCall(PetscDrawLGDraw(lg));
400   PetscCall(PetscDrawLGDestroy(&lg));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
405 {
406   DM dm;
407 
408   PetscFunctionBegin;
409   PetscCall(VecGetDM(u, &dm));
410   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
411   PetscFunctionReturn(PETSC_SUCCESS);
412 }
413 
414 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
415 {
416   DM                 dm;
417   PetscSection       s;
418   PetscDraw          draw, popup;
419   DM                 cdm;
420   PetscSection       coordSection;
421   Vec                coordinates;
422   const PetscScalar *array;
423   PetscReal          lbound[3], ubound[3];
424   PetscReal          vbound[2], time;
425   PetscBool          flg;
426   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
427   const char        *name;
428   char               title[PETSC_MAX_PATH_LEN];
429 
430   PetscFunctionBegin;
431   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
432   PetscCall(VecGetDM(v, &dm));
433   PetscCall(DMGetCoordinateDim(dm, &dim));
434   PetscCall(DMGetLocalSection(dm, &s));
435   PetscCall(PetscSectionGetNumFields(s, &Nf));
436   PetscCall(DMGetCoarsenLevel(dm, &level));
437   PetscCall(DMGetCoordinateDM(dm, &cdm));
438   PetscCall(DMGetLocalSection(cdm, &coordSection));
439   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
440   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
441   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
442 
443   PetscCall(PetscObjectGetName((PetscObject)v, &name));
444   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
445 
446   PetscCall(VecGetLocalSize(coordinates, &N));
447   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
448   PetscCall(PetscDrawClear(draw));
449 
450   /* Could implement something like DMDASelectFields() */
451   for (f = 0; f < Nf; ++f) {
452     DM          fdm = dm;
453     Vec         fv  = v;
454     IS          fis;
455     char        prefix[PETSC_MAX_PATH_LEN];
456     const char *fname;
457 
458     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
459     PetscCall(PetscSectionGetFieldName(s, f, &fname));
460 
461     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
462     else prefix[0] = '\0';
463     if (Nf > 1) {
464       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
465       PetscCall(VecGetSubVector(v, fis, &fv));
466       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
467       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
468     }
469     for (comp = 0; comp < Nc; ++comp, ++w) {
470       PetscInt nmax = 2;
471 
472       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
473       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
474       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
475       PetscCall(PetscDrawSetTitle(draw, title));
476 
477       /* TODO Get max and min only for this component */
478       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
479       if (!flg) {
480         PetscCall(VecMin(fv, NULL, &vbound[0]));
481         PetscCall(VecMax(fv, NULL, &vbound[1]));
482         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
483       }
484 
485       PetscCall(PetscDrawGetPopup(draw, &popup));
486       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
487       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
488       PetscCall(VecGetArrayRead(fv, &array));
489       for (c = cStart; c < cEnd; ++c) {
490         DMPolytopeType     ct;
491         PetscScalar       *coords = NULL, *a = NULL;
492         const PetscScalar *coords_arr;
493         PetscBool          isDG;
494         PetscInt           numCoords;
495         int                color[4] = {-1, -1, -1, -1};
496 
497         PetscCall(DMPlexGetCellType(dm, c, &ct));
498         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
499         if (a) {
500           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
501           color[1] = color[2] = color[3] = color[0];
502         } else {
503           PetscScalar *vals = NULL;
504           PetscInt     numVals, va;
505 
506           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
507           if (!numVals) {
508             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
509             continue;
510           }
511           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
512           switch (numVals / Nc) {
513           case 1: /* P1 Clamped Segment Prism */
514           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
515             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
516             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
517             break;
518           case 3: /* P1 Triangle */
519           case 4: /* P1 Quadrangle */
520             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
521             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
522             break;
523           case 6: /* P2 Triangle */
524           case 8: /* P2 Quadrangle */
525             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
526             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
527             break;
528           default:
529             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
530           }
531           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
532         }
533         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
534         switch (numCoords) {
535         case 6:
536         case 12: /* Localized triangle */
537           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
538           break;
539         case 8:
540         case 16: /* Localized quadrilateral */
541           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
542             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
543           } else {
544             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
545             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
546           }
547           break;
548         default:
549           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
550         }
551         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
552       }
553       PetscCall(VecRestoreArrayRead(fv, &array));
554       PetscCall(PetscDrawFlush(draw));
555       PetscCall(PetscDrawPause(draw));
556       PetscCall(PetscDrawSave(draw));
557     }
558     if (Nf > 1) {
559       PetscCall(VecRestoreSubVector(v, fis, &fv));
560       PetscCall(ISDestroy(&fis));
561       PetscCall(DMDestroy(&fdm));
562     }
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscDraw draw;
571   PetscInt  dim;
572   PetscBool isnull;
573 
574   PetscFunctionBegin;
575   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
576   PetscCall(PetscDrawIsNull(draw, &isnull));
577   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
578 
579   PetscCall(VecGetDM(v, &dm));
580   PetscCall(DMGetCoordinateDim(dm, &dim));
581   switch (dim) {
582   case 1:
583     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
584     break;
585   case 2:
586     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
587     break;
588   default:
589     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
590   }
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
595 {
596   DM                      dm;
597   Vec                     locv;
598   const char             *name;
599   PetscSection            section;
600   PetscInt                pStart, pEnd;
601   PetscInt                numFields;
602   PetscViewerVTKFieldType ft;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
607   PetscCall(PetscObjectGetName((PetscObject)v, &name));
608   PetscCall(PetscObjectSetName((PetscObject)locv, name));
609   PetscCall(VecCopy(v, locv));
610   PetscCall(DMGetLocalSection(dm, &section));
611   PetscCall(PetscSectionGetNumFields(section, &numFields));
612   if (!numFields) {
613     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
614     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
615   } else {
616     PetscInt f;
617 
618     for (f = 0; f < numFields; f++) {
619       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
620       if (ft == PETSC_VTK_INVALID) continue;
621       PetscCall(PetscObjectReference((PetscObject)locv));
622       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
623     }
624     PetscCall(VecDestroy(&locv));
625   }
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
641   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
642   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
643   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
644     PetscInt    i, numFields;
645     PetscObject fe;
646     PetscBool   fem  = PETSC_FALSE;
647     Vec         locv = v;
648     const char *name;
649     PetscInt    step;
650     PetscReal   time;
651 
652     PetscCall(DMGetNumFields(dm, &numFields));
653     for (i = 0; i < numFields; i++) {
654       PetscCall(DMGetField(dm, i, NULL, &fe));
655       if (fe->classid == PETSCFE_CLASSID) {
656         fem = PETSC_TRUE;
657         break;
658       }
659     }
660     if (fem) {
661       PetscObject isZero;
662 
663       PetscCall(DMGetLocalVector(dm, &locv));
664       PetscCall(PetscObjectGetName((PetscObject)v, &name));
665       PetscCall(PetscObjectSetName((PetscObject)locv, name));
666       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
667       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
668       PetscCall(VecCopy(v, locv));
669       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
670       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
671     }
672     if (isvtk) {
673       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
674     } else if (ishdf5) {
675 #if defined(PETSC_HAVE_HDF5)
676       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
677 #else
678       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680     } else if (isdraw) {
681       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
682     } else if (ispython) {
683       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
684     } else if (isglvis) {
685       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
686       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
687       PetscCall(VecView_GLVis(locv, viewer));
688     } else if (iscgns) {
689 #if defined(PETSC_HAVE_CGNS)
690       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
691 #else
692       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
693 #endif
694     }
695     if (fem) {
696       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
697       PetscCall(DMRestoreLocalVector(dm, &locv));
698     }
699   } else {
700     PetscBool isseq;
701 
702     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
703     if (isseq) PetscCall(VecView_Seq(v, viewer));
704     else PetscCall(VecView_MPI(v, viewer));
705   }
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
710 {
711   DM        dm;
712   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
713 
714   PetscFunctionBegin;
715   PetscCall(VecGetDM(v, &dm));
716   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
723   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
724   if (isvtk || isdraw || isglvis || iscgns || ispython) {
725     Vec         locv;
726     PetscObject isZero;
727     const char *name;
728 
729     PetscCall(DMGetLocalVector(dm, &locv));
730     PetscCall(PetscObjectGetName((PetscObject)v, &name));
731     PetscCall(PetscObjectSetName((PetscObject)locv, name));
732     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
733     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
734     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
735     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
736     PetscCall(VecView_Plex_Local(locv, viewer));
737     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
738     PetscCall(DMRestoreLocalVector(dm, &locv));
739   } else if (ishdf5) {
740 #if defined(PETSC_HAVE_HDF5)
741     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
742 #else
743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
744 #endif
745   } else if (isexodusii) {
746 #if defined(PETSC_HAVE_EXODUSII)
747     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
748 #else
749     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
750 #endif
751   } else {
752     PetscBool isseq;
753 
754     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
755     if (isseq) PetscCall(VecView_Seq(v, viewer));
756     else PetscCall(VecView_MPI(v, viewer));
757   }
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
762 {
763   DM                dm;
764   MPI_Comm          comm;
765   PetscViewerFormat format;
766   Vec               v;
767   PetscBool         isvtk, ishdf5;
768 
769   PetscFunctionBegin;
770   PetscCall(VecGetDM(originalv, &dm));
771   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
772   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
773   PetscCall(PetscViewerGetFormat(viewer, &format));
774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
776   if (format == PETSC_VIEWER_NATIVE) {
777     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
778     /* this need a better fix */
779     if (dm->useNatural) {
780       if (dm->sfNatural) {
781         const char *vecname;
782         PetscInt    n, nroots;
783 
784         PetscCall(VecGetLocalSize(originalv, &n));
785         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
786         if (n == nroots) {
787           PetscCall(DMPlexCreateNaturalVector(dm, &v));
788           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
789           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
790           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
791           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
792         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
793       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
794     } else v = originalv;
795   } else v = originalv;
796 
797   if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
800 #else
801     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
802 #endif
803   } else if (isvtk) {
804     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
805   } else {
806     PetscBool isseq;
807 
808     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
809     if (isseq) PetscCall(VecView_Seq(v, viewer));
810     else PetscCall(VecView_MPI(v, viewer));
811   }
812   if (v != originalv) PetscCall(VecDestroy(&v));
813   PetscFunctionReturn(PETSC_SUCCESS);
814 }
815 
816 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
817 {
818   DM        dm;
819   PetscBool ishdf5;
820 
821   PetscFunctionBegin;
822   PetscCall(VecGetDM(v, &dm));
823   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
824   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
825   if (ishdf5) {
826     DM          dmBC;
827     Vec         gv;
828     const char *name;
829 
830     PetscCall(DMGetOutputDM(dm, &dmBC));
831     PetscCall(DMGetGlobalVector(dmBC, &gv));
832     PetscCall(PetscObjectGetName((PetscObject)v, &name));
833     PetscCall(PetscObjectSetName((PetscObject)gv, name));
834     PetscCall(VecLoad_Default(gv, viewer));
835     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
836     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
837     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
838   } else PetscCall(VecLoad_Default(v, viewer));
839   PetscFunctionReturn(PETSC_SUCCESS);
840 }
841 
842 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
843 {
844   DM        dm;
845   PetscBool ishdf5, isexodusii, iscgns;
846 
847   PetscFunctionBegin;
848   PetscCall(VecGetDM(v, &dm));
849   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
850   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
851   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
852   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
853   if (ishdf5) {
854 #if defined(PETSC_HAVE_HDF5)
855     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
856 #else
857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
858 #endif
859   } else if (isexodusii) {
860 #if defined(PETSC_HAVE_EXODUSII)
861     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
862 #else
863     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
864 #endif
865   } else if (iscgns) {
866 #if defined(PETSC_HAVE_CGNS)
867     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
868 #else
869     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
870 #endif
871   } else PetscCall(VecLoad_Default(v, viewer));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
876 {
877   DM                dm;
878   PetscViewerFormat format;
879   PetscBool         ishdf5;
880 
881   PetscFunctionBegin;
882   PetscCall(VecGetDM(originalv, &dm));
883   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
884   PetscCall(PetscViewerGetFormat(viewer, &format));
885   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
886   if (format == PETSC_VIEWER_NATIVE) {
887     if (dm->useNatural) {
888       if (dm->sfNatural) {
889         if (ishdf5) {
890 #if defined(PETSC_HAVE_HDF5)
891           Vec         v;
892           const char *vecname;
893 
894           PetscCall(DMPlexCreateNaturalVector(dm, &v));
895           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
896           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
897           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
898           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
899           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
900           PetscCall(VecDestroy(&v));
901 #else
902           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
903 #endif
904         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
905       }
906     } else PetscCall(VecLoad_Default(originalv, viewer));
907   }
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
912 {
913   PetscSection       coordSection;
914   Vec                coordinates;
915   DMLabel            depthLabel, celltypeLabel;
916   const char        *name[4];
917   const PetscScalar *a;
918   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
919 
920   PetscFunctionBegin;
921   PetscCall(DMGetDimension(dm, &dim));
922   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
923   PetscCall(DMGetCoordinateSection(dm, &coordSection));
924   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
925   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
926   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
927   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
928   PetscCall(VecGetArrayRead(coordinates, &a));
929   name[0]       = "vertex";
930   name[1]       = "edge";
931   name[dim - 1] = "face";
932   name[dim]     = "cell";
933   for (c = cStart; c < cEnd; ++c) {
934     PetscInt *closure = NULL;
935     PetscInt  closureSize, cl, ct;
936 
937     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
938     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
939     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
940     PetscCall(PetscViewerASCIIPushTab(viewer));
941     for (cl = 0; cl < closureSize * 2; cl += 2) {
942       PetscInt point = closure[cl], depth, dof, off, d, p;
943 
944       if ((point < pStart) || (point >= pEnd)) continue;
945       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
946       if (!dof) continue;
947       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
948       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
949       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
950       for (p = 0; p < dof / dim; ++p) {
951         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
952         for (d = 0; d < dim; ++d) {
953           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
954           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
955         }
956         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
957       }
958       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
959     }
960     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
961     PetscCall(PetscViewerASCIIPopTab(viewer));
962   }
963   PetscCall(VecRestoreArrayRead(coordinates, &a));
964   PetscFunctionReturn(PETSC_SUCCESS);
965 }
966 
967 typedef enum {
968   CS_CARTESIAN,
969   CS_POLAR,
970   CS_CYLINDRICAL,
971   CS_SPHERICAL
972 } CoordSystem;
973 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
974 
975 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
976 {
977   PetscInt i;
978 
979   PetscFunctionBegin;
980   if (dim > 3) {
981     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
982   } else {
983     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
984 
985     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
986     switch (cs) {
987     case CS_CARTESIAN:
988       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
989       break;
990     case CS_POLAR:
991       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
992       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
993       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
994       break;
995     case CS_CYLINDRICAL:
996       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
997       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
998       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
999       trcoords[2] = coords[2];
1000       break;
1001     case CS_SPHERICAL:
1002       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
1003       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1004       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1005       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1006       break;
1007     }
1008     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1009   }
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1014 {
1015   DM_Plex          *mesh = (DM_Plex *)dm->data;
1016   DM                cdm, cdmCell;
1017   PetscSection      coordSection, coordSectionCell;
1018   Vec               coordinates, coordinatesCell;
1019   PetscViewerFormat format;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscViewerGetFormat(viewer, &format));
1023   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1024     const char *name;
1025     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1026     PetscInt    pStart, pEnd, p, numLabels, l;
1027     PetscMPIInt rank, size;
1028 
1029     PetscCall(DMGetCoordinateDM(dm, &cdm));
1030     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1031     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1032     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1033     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1034     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1035     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1036     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1037     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1039     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1040     PetscCall(DMGetDimension(dm, &dim));
1041     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1042     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1043     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1044     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1045     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1046     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1047     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1048     for (p = pStart; p < pEnd; ++p) {
1049       PetscInt dof, off, s;
1050 
1051       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1052       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1053       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1054     }
1055     PetscCall(PetscViewerFlush(viewer));
1056     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1057     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1058     for (p = pStart; p < pEnd; ++p) {
1059       PetscInt dof, off, c;
1060 
1061       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1062       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1063       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
1064     }
1065     PetscCall(PetscViewerFlush(viewer));
1066     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1067     if (coordSection && coordinates) {
1068       CoordSystem        cs = CS_CARTESIAN;
1069       const PetscScalar *array, *arrayCell = NULL;
1070       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1071       PetscMPIInt        rank;
1072       const char        *name;
1073 
1074       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1075       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1076       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1077       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1078       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1079       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1080       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1081       pStart = PetscMin(pvStart, pcStart);
1082       pEnd   = PetscMax(pvEnd, pcEnd);
1083       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1084       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1085       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1086       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1087 
1088       PetscCall(VecGetArrayRead(coordinates, &array));
1089       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1090       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1091       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1092       for (p = pStart; p < pEnd; ++p) {
1093         PetscInt dof, off;
1094 
1095         if (p >= pvStart && p < pvEnd) {
1096           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1097           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1098           if (dof) {
1099             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1100             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1101             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1102           }
1103         }
1104         if (cdmCell && p >= pcStart && p < pcEnd) {
1105           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1106           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1107           if (dof) {
1108             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1109             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1110             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1111           }
1112         }
1113       }
1114       PetscCall(PetscViewerFlush(viewer));
1115       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1116       PetscCall(VecRestoreArrayRead(coordinates, &array));
1117       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1118     }
1119     PetscCall(DMGetNumLabels(dm, &numLabels));
1120     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1121     for (l = 0; l < numLabels; ++l) {
1122       DMLabel     label;
1123       PetscBool   isdepth;
1124       const char *name;
1125 
1126       PetscCall(DMGetLabelName(dm, l, &name));
1127       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1128       if (isdepth) continue;
1129       PetscCall(DMGetLabel(dm, name, &label));
1130       PetscCall(DMLabelView(label, viewer));
1131     }
1132     if (size > 1) {
1133       PetscSF sf;
1134 
1135       PetscCall(DMGetPointSF(dm, &sf));
1136       PetscCall(PetscSFView(sf, viewer));
1137     }
1138     if (mesh->periodic.face_sfs)
1139       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1140     PetscCall(PetscViewerFlush(viewer));
1141   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1142     const char  *name, *color;
1143     const char  *defcolors[3]  = {"gray", "orange", "green"};
1144     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1145     char         lname[PETSC_MAX_PATH_LEN];
1146     PetscReal    scale      = 2.0;
1147     PetscReal    tikzscale  = 1.0;
1148     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1149     double       tcoords[3];
1150     PetscScalar *coords;
1151     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1152     PetscMPIInt  rank, size;
1153     char       **names, **colors, **lcolors;
1154     PetscBool    flg, lflg;
1155     PetscBT      wp = NULL;
1156     PetscInt     pEnd, pStart;
1157 
1158     PetscCall(DMGetCoordinateDM(dm, &cdm));
1159     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1160     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1161     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1162     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1163     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1164     PetscCall(DMGetDimension(dm, &dim));
1165     PetscCall(DMPlexGetDepth(dm, &depth));
1166     PetscCall(DMGetNumLabels(dm, &numLabels));
1167     numLabels  = PetscMax(numLabels, 10);
1168     numColors  = 10;
1169     numLColors = 10;
1170     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1171     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1172     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1173     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1174     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1175     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1176     n = 4;
1177     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1178     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1179     n = 4;
1180     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1181     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1182     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1183     if (!useLabels) numLabels = 0;
1184     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1185     if (!useColors) {
1186       numColors = 3;
1187       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1188     }
1189     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1190     if (!useColors) {
1191       numLColors = 4;
1192       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1193     }
1194     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1195     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1196     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1197     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1198     if (depth < dim) plotEdges = PETSC_FALSE;
1199     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1200 
1201     /* filter points with labelvalue != labeldefaultvalue */
1202     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1203     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1204     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1205     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1206     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1207     if (lflg) {
1208       DMLabel lbl;
1209 
1210       PetscCall(DMGetLabel(dm, lname, &lbl));
1211       if (lbl) {
1212         PetscInt val, defval;
1213 
1214         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1215         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1216         for (c = pStart; c < pEnd; c++) {
1217           PetscInt *closure = NULL;
1218           PetscInt  closureSize;
1219 
1220           PetscCall(DMLabelGetValue(lbl, c, &val));
1221           if (val == defval) continue;
1222 
1223           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229 
1230     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1231     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1232     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1233     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1234 \\documentclass[tikz]{standalone}\n\n\
1235 \\usepackage{pgflibraryshapes}\n\
1236 \\usetikzlibrary{backgrounds}\n\
1237 \\usetikzlibrary{arrows}\n\
1238 \\begin{document}\n"));
1239     if (size > 1) {
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1241       for (p = 0; p < size; ++p) {
1242         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1243         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1244       }
1245       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1246     }
1247     if (drawHasse) {
1248       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1249 
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1264       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1265       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1266     }
1267     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1268 
1269     /* Plot vertices */
1270     PetscCall(VecGetArray(coordinates, &coords));
1271     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1272     for (v = vStart; v < vEnd; ++v) {
1273       PetscInt  off, dof, d;
1274       PetscBool isLabeled = PETSC_FALSE;
1275 
1276       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1277       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1278       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1279       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1280       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1281       for (d = 0; d < dof; ++d) {
1282         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1283         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1284       }
1285       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1286       if (dim == 3) {
1287         PetscReal tmp = tcoords[1];
1288         tcoords[1]    = tcoords[2];
1289         tcoords[2]    = -tmp;
1290       }
1291       for (d = 0; d < dof; ++d) {
1292         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1293         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1294       }
1295       if (drawHasse) color = colors[0 % numColors];
1296       else color = colors[rank % numColors];
1297       for (l = 0; l < numLabels; ++l) {
1298         PetscInt val;
1299         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1300         if (val >= 0) {
1301           color     = lcolors[l % numLColors];
1302           isLabeled = PETSC_TRUE;
1303           break;
1304         }
1305       }
1306       if (drawNumbers[0]) {
1307         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1308       } else if (drawColors[0]) {
1309         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1310       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1311     }
1312     PetscCall(VecRestoreArray(coordinates, &coords));
1313     PetscCall(PetscViewerFlush(viewer));
1314     /* Plot edges */
1315     if (plotEdges) {
1316       PetscCall(VecGetArray(coordinates, &coords));
1317       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1318       for (e = eStart; e < eEnd; ++e) {
1319         const PetscInt *cone;
1320         PetscInt        coneSize, offA, offB, dof, d;
1321 
1322         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1323         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1324         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1325         PetscCall(DMPlexGetCone(dm, e, &cone));
1326         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1327         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1328         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1329         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1330         for (d = 0; d < dof; ++d) {
1331           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1332           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1333         }
1334         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1335         if (dim == 3) {
1336           PetscReal tmp = tcoords[1];
1337           tcoords[1]    = tcoords[2];
1338           tcoords[2]    = -tmp;
1339         }
1340         for (d = 0; d < dof; ++d) {
1341           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1342           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1343         }
1344         if (drawHasse) color = colors[1 % numColors];
1345         else color = colors[rank % numColors];
1346         for (l = 0; l < numLabels; ++l) {
1347           PetscInt val;
1348           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1349           if (val >= 0) {
1350             color = lcolors[l % numLColors];
1351             break;
1352           }
1353         }
1354         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1355       }
1356       PetscCall(VecRestoreArray(coordinates, &coords));
1357       PetscCall(PetscViewerFlush(viewer));
1358       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1359     }
1360     /* Plot cells */
1361     if (dim == 3 || !drawNumbers[1]) {
1362       for (e = eStart; e < eEnd; ++e) {
1363         const PetscInt *cone;
1364 
1365         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1366         color = colors[rank % numColors];
1367         for (l = 0; l < numLabels; ++l) {
1368           PetscInt val;
1369           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1370           if (val >= 0) {
1371             color = lcolors[l % numLColors];
1372             break;
1373           }
1374         }
1375         PetscCall(DMPlexGetCone(dm, e, &cone));
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1377       }
1378     } else {
1379       DMPolytopeType ct;
1380 
1381       /* Drawing a 2D polygon */
1382       for (c = cStart; c < cEnd; ++c) {
1383         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1384         PetscCall(DMPlexGetCellType(dm, c, &ct));
1385         if (DMPolytopeTypeIsHybrid(ct)) {
1386           const PetscInt *cone;
1387           PetscInt        coneSize, e;
1388 
1389           PetscCall(DMPlexGetCone(dm, c, &cone));
1390           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1391           for (e = 0; e < coneSize; ++e) {
1392             const PetscInt *econe;
1393 
1394             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1395             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1396           }
1397         } else {
1398           PetscInt *closure = NULL;
1399           PetscInt  closureSize, Nv = 0, v;
1400 
1401           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1402           for (p = 0; p < closureSize * 2; p += 2) {
1403             const PetscInt point = closure[p];
1404 
1405             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1406           }
1407           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1408           for (v = 0; v <= Nv; ++v) {
1409             const PetscInt vertex = closure[v % Nv];
1410 
1411             if (v > 0) {
1412               if (plotEdges) {
1413                 const PetscInt *edge;
1414                 PetscInt        endpoints[2], ne;
1415 
1416                 endpoints[0] = closure[v - 1];
1417                 endpoints[1] = vertex;
1418                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1419                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1420                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1421                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1422               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1423             }
1424             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1425           }
1426           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1427           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1428         }
1429       }
1430     }
1431     for (c = cStart; c < cEnd; ++c) {
1432       double             ccoords[3] = {0.0, 0.0, 0.0};
1433       PetscBool          isLabeled  = PETSC_FALSE;
1434       PetscScalar       *cellCoords = NULL;
1435       const PetscScalar *array;
1436       PetscInt           numCoords, cdim, d;
1437       PetscBool          isDG;
1438 
1439       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1440       PetscCall(DMGetCoordinateDim(dm, &cdim));
1441       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1442       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1443       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1444       for (p = 0; p < numCoords / cdim; ++p) {
1445         for (d = 0; d < cdim; ++d) {
1446           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1447           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1448         }
1449         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1450         if (cdim == 3) {
1451           PetscReal tmp = tcoords[1];
1452           tcoords[1]    = tcoords[2];
1453           tcoords[2]    = -tmp;
1454         }
1455         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1456       }
1457       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1458       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1459       for (d = 0; d < cdim; ++d) {
1460         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1461         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1462       }
1463       if (drawHasse) color = colors[depth % numColors];
1464       else color = colors[rank % numColors];
1465       for (l = 0; l < numLabels; ++l) {
1466         PetscInt val;
1467         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1468         if (val >= 0) {
1469           color     = lcolors[l % numLColors];
1470           isLabeled = PETSC_TRUE;
1471           break;
1472         }
1473       }
1474       if (drawNumbers[dim]) {
1475         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1476       } else if (drawColors[dim]) {
1477         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1478       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1479     }
1480     if (drawHasse) {
1481       int height = 0;
1482 
1483       color = colors[depth % numColors];
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1485       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1487       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1489 
1490       if (depth > 2) {
1491         color = colors[1 % numColors];
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1496         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1497       }
1498 
1499       color = colors[1 % numColors];
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1501       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1502       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1503       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1504       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1505 
1506       color = colors[0 % numColors];
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1508       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1509       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1510       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1511       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1512 
1513       for (p = pStart; p < pEnd; ++p) {
1514         const PetscInt *cone;
1515         PetscInt        coneSize, cp;
1516 
1517         PetscCall(DMPlexGetCone(dm, p, &cone));
1518         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1519         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1520       }
1521     }
1522     PetscCall(PetscViewerFlush(viewer));
1523     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1524     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1526     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1527     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1528     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1529     PetscCall(PetscFree3(names, colors, lcolors));
1530     PetscCall(PetscBTDestroy(&wp));
1531   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1532     Vec                    cown, acown;
1533     VecScatter             sct;
1534     ISLocalToGlobalMapping g2l;
1535     IS                     gid, acis;
1536     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1537     MPI_Group              ggroup, ngroup;
1538     PetscScalar           *array, nid;
1539     const PetscInt        *idxs;
1540     PetscInt              *idxs2, *start, *adjacency, *work;
1541     PetscInt64             lm[3], gm[3];
1542     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1543     PetscMPIInt            d1, d2, rank;
1544 
1545     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1546     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1547 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1548     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1549 #endif
1550     if (ncomm != MPI_COMM_NULL) {
1551       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1552       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1553       d1 = 0;
1554       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1555       nid = d2;
1556       PetscCallMPI(MPI_Group_free(&ggroup));
1557       PetscCallMPI(MPI_Group_free(&ngroup));
1558       PetscCallMPI(MPI_Comm_free(&ncomm));
1559     } else nid = 0.0;
1560 
1561     /* Get connectivity */
1562     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1563     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1564 
1565     /* filter overlapped local cells */
1566     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1567     PetscCall(ISGetIndices(gid, &idxs));
1568     PetscCall(ISGetLocalSize(gid, &cum));
1569     PetscCall(PetscMalloc1(cum, &idxs2));
1570     for (c = cStart, cum = 0; c < cEnd; c++) {
1571       if (idxs[c - cStart] < 0) continue;
1572       idxs2[cum++] = idxs[c - cStart];
1573     }
1574     PetscCall(ISRestoreIndices(gid, &idxs));
1575     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1576     PetscCall(ISDestroy(&gid));
1577     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1578 
1579     /* support for node-aware cell locality */
1580     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1581     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1582     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1583     PetscCall(VecGetArray(cown, &array));
1584     for (c = 0; c < numVertices; c++) array[c] = nid;
1585     PetscCall(VecRestoreArray(cown, &array));
1586     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1587     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1588     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1589     PetscCall(ISDestroy(&acis));
1590     PetscCall(VecScatterDestroy(&sct));
1591     PetscCall(VecDestroy(&cown));
1592 
1593     /* compute edgeCut */
1594     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1595     PetscCall(PetscMalloc1(cum, &work));
1596     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1597     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1598     PetscCall(ISDestroy(&gid));
1599     PetscCall(VecGetArray(acown, &array));
1600     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1601       PetscInt totl;
1602 
1603       totl = start[c + 1] - start[c];
1604       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1605       for (i = 0; i < totl; i++) {
1606         if (work[i] < 0) {
1607           ect += 1;
1608           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1609         }
1610       }
1611     }
1612     PetscCall(PetscFree(work));
1613     PetscCall(VecRestoreArray(acown, &array));
1614     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1615     lm[1] = -numVertices;
1616     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1617     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1618     lm[0] = ect;                     /* edgeCut */
1619     lm[1] = ectn;                    /* node-aware edgeCut */
1620     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1621     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1622     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1623 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1624     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1625 #else
1626     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1627 #endif
1628     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1629     PetscCall(PetscFree(start));
1630     PetscCall(PetscFree(adjacency));
1631     PetscCall(VecDestroy(&acown));
1632   } else {
1633     const char    *name;
1634     PetscInt      *sizes, *hybsizes, *ghostsizes;
1635     PetscInt       locDepth, depth, cellHeight, dim, d;
1636     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1637     PetscInt       numLabels, l, maxSize = 17;
1638     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1639     MPI_Comm       comm;
1640     PetscMPIInt    size, rank;
1641 
1642     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1643     PetscCallMPI(MPI_Comm_size(comm, &size));
1644     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1645     PetscCall(DMGetDimension(dm, &dim));
1646     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1647     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1648     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1649     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1650     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1651     PetscCall(DMPlexGetDepth(dm, &locDepth));
1652     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1653     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1654     gcNum = gcEnd - gcStart;
1655     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1656     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1657     for (d = 0; d <= depth; d++) {
1658       PetscInt Nc[2] = {0, 0}, ict;
1659 
1660       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1661       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1662       ict = ct0;
1663       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1664       ct0 = (DMPolytopeType)ict;
1665       for (p = pStart; p < pEnd; ++p) {
1666         DMPolytopeType ct;
1667 
1668         PetscCall(DMPlexGetCellType(dm, p, &ct));
1669         if (ct == ct0) ++Nc[0];
1670         else ++Nc[1];
1671       }
1672       if (size < maxSize) {
1673         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1674         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1675         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1676         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1677         for (p = 0; p < size; ++p) {
1678           if (rank == 0) {
1679             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1680             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1681             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1682           }
1683         }
1684       } else {
1685         PetscInt locMinMax[2];
1686 
1687         locMinMax[0] = Nc[0] + Nc[1];
1688         locMinMax[1] = Nc[0] + Nc[1];
1689         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1690         locMinMax[0] = Nc[1];
1691         locMinMax[1] = Nc[1];
1692         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1693         if (d == depth) {
1694           locMinMax[0] = gcNum;
1695           locMinMax[1] = gcNum;
1696           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1697         }
1698         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1699         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1700         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1701         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1702       }
1703       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1704     }
1705     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1706     {
1707       const PetscReal *maxCell;
1708       const PetscReal *L;
1709       PetscBool        localized;
1710 
1711       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1712       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1713       if (L || localized) {
1714         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1715         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1716         if (L) {
1717           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1718           for (d = 0; d < dim; ++d) {
1719             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1720             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1721           }
1722           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1723         }
1724         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1725         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1726       }
1727     }
1728     PetscCall(DMGetNumLabels(dm, &numLabels));
1729     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1730     for (l = 0; l < numLabels; ++l) {
1731       DMLabel     label;
1732       const char *name;
1733       PetscInt   *values;
1734       PetscInt    numValues, v;
1735 
1736       PetscCall(DMGetLabelName(dm, l, &name));
1737       PetscCall(DMGetLabel(dm, name, &label));
1738       PetscCall(DMLabelGetNumValues(label, &numValues));
1739       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1740 
1741       { // Extract array of DMLabel values so it can be sorted
1742         IS              is_values;
1743         const PetscInt *is_values_local = NULL;
1744 
1745         PetscCall(DMLabelGetValueIS(label, &is_values));
1746         PetscCall(ISGetIndices(is_values, &is_values_local));
1747         PetscCall(PetscMalloc1(numValues, &values));
1748         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1749         PetscCall(PetscSortInt(numValues, values));
1750         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1751         PetscCall(ISDestroy(&is_values));
1752       }
1753       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1754       for (v = 0; v < numValues; ++v) {
1755         PetscInt size;
1756 
1757         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1758         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1759         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1760       }
1761       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1762       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1763       PetscCall(PetscFree(values));
1764     }
1765     {
1766       char    **labelNames;
1767       PetscInt  Nl = numLabels;
1768       PetscBool flg;
1769 
1770       PetscCall(PetscMalloc1(Nl, &labelNames));
1771       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1772       for (l = 0; l < Nl; ++l) {
1773         DMLabel label;
1774 
1775         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1776         if (flg) {
1777           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1778           PetscCall(DMLabelView(label, viewer));
1779         }
1780         PetscCall(PetscFree(labelNames[l]));
1781       }
1782       PetscCall(PetscFree(labelNames));
1783     }
1784     /* If no fields are specified, people do not want to see adjacency */
1785     if (dm->Nf) {
1786       PetscInt f;
1787 
1788       for (f = 0; f < dm->Nf; ++f) {
1789         const char *name;
1790 
1791         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1792         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1793         PetscCall(PetscViewerASCIIPushTab(viewer));
1794         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1795         if (dm->fields[f].adjacency[0]) {
1796           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1797           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1798         } else {
1799           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1800           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1801         }
1802         PetscCall(PetscViewerASCIIPopTab(viewer));
1803       }
1804     }
1805     DMPlexTransform tr;
1806 
1807     PetscCall(DMPlexGetTransform(dm, &tr));
1808     if (tr) {
1809       PetscCall(PetscViewerASCIIPushTab(viewer));
1810       PetscCall(PetscViewerASCIIPrintf(viewer, "Created using transform:\n"));
1811       PetscCall(DMPlexTransformView(tr, viewer));
1812       PetscCall(PetscViewerASCIIPopTab(viewer));
1813     }
1814     PetscCall(DMGetCoarseDM(dm, &cdm));
1815     if (cdm) {
1816       PetscCall(PetscViewerASCIIPushTab(viewer));
1817       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1818       PetscCall(DMPlexView_Ascii(cdm, viewer));
1819       PetscCall(PetscViewerASCIIPopTab(viewer));
1820     }
1821   }
1822   PetscFunctionReturn(PETSC_SUCCESS);
1823 }
1824 
1825 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt lC, PetscInt cC, PetscInt cell, const PetscScalar coords[])
1826 {
1827   DMPolytopeType ct;
1828   PetscMPIInt    rank;
1829   PetscInt       cdim;
1830   int            lineColor, cellColor;
1831 
1832   PetscFunctionBegin;
1833   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1834   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1835   PetscCall(DMGetCoordinateDim(dm, &cdim));
1836   lineColor = (int)(lC < 0 ? PETSC_DRAW_BLACK : lC);
1837   cellColor = (int)(cC < 0 ? PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2 : cC);
1838   switch (ct) {
1839   case DM_POLYTOPE_SEGMENT:
1840   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1841     switch (cdim) {
1842     case 1: {
1843       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1844       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1845 
1846       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, lineColor));
1847       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, lineColor));
1848       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, lineColor));
1849     } break;
1850     case 2: {
1851       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1852       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1853       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1854 
1855       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1856       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, lineColor));
1857       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, lineColor));
1858     } break;
1859     default:
1860       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1861     }
1862     break;
1863   case DM_POLYTOPE_TRIANGLE:
1864     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1865     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1866     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1867     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1868     break;
1869   case DM_POLYTOPE_QUADRILATERAL:
1870     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1871     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), cellColor, cellColor, cellColor));
1872     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1873     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1874     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1875     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1876     break;
1877   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1878     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1879     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1880     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1881     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1882     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1883     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1884     break;
1885   case DM_POLYTOPE_FV_GHOST:
1886     break;
1887   default:
1888     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1889   }
1890   PetscFunctionReturn(PETSC_SUCCESS);
1891 }
1892 
1893 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1894 {
1895   PetscReal   centroid[2] = {0., 0.};
1896   PetscMPIInt rank;
1897   PetscMPIInt fillColor;
1898 
1899   PetscFunctionBegin;
1900   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1901   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1902   for (PetscInt v = 0; v < Nv; ++v) {
1903     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1904     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1905   }
1906   for (PetscInt e = 0; e < Nv; ++e) {
1907     refCoords[0] = refVertices[e * 2 + 0];
1908     refCoords[1] = refVertices[e * 2 + 1];
1909     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1910       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1911       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1912     }
1913     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1914     for (PetscInt d = 0; d < edgeDiv; ++d) {
1915       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));
1916       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1917     }
1918   }
1919   PetscFunctionReturn(PETSC_SUCCESS);
1920 }
1921 
1922 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1923 {
1924   DMPolytopeType ct;
1925 
1926   PetscFunctionBegin;
1927   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1928   switch (ct) {
1929   case DM_POLYTOPE_TRIANGLE: {
1930     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1931 
1932     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1933   } break;
1934   case DM_POLYTOPE_QUADRILATERAL: {
1935     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1936 
1937     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1938   } break;
1939   default:
1940     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1941   }
1942   PetscFunctionReturn(PETSC_SUCCESS);
1943 }
1944 
1945 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1946 {
1947   PetscDraw    draw;
1948   DM           cdm;
1949   PetscSection coordSection;
1950   Vec          coordinates;
1951   PetscReal    xyl[3], xyr[3];
1952   PetscReal   *refCoords, *edgeCoords;
1953   PetscBool    isnull, drawAffine;
1954   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv, lineColor = PETSC_DETERMINE, cellColor = PETSC_DETERMINE;
1955 
1956   PetscFunctionBegin;
1957   PetscCall(DMGetCoordinateDim(dm, &dim));
1958   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1959   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1960   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1961   edgeDiv    = cDegree + 1;
1962   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_line_color", &lineColor, NULL));
1963   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_cell_color", &cellColor, NULL));
1964   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1965   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1966   PetscCall(DMGetCoordinateDM(dm, &cdm));
1967   PetscCall(DMGetLocalSection(cdm, &coordSection));
1968   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1969   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1970   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1971 
1972   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1973   PetscCall(PetscDrawIsNull(draw, &isnull));
1974   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1975   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1976 
1977   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1978   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1979   PetscCall(PetscDrawClear(draw));
1980 
1981   for (c = cStart; c < cEnd; ++c) {
1982     PetscScalar       *coords = NULL;
1983     const PetscScalar *coords_arr;
1984     PetscInt           numCoords;
1985     PetscBool          isDG;
1986 
1987     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1988     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, lineColor, cellColor, c, coords));
1989     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1990     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1991   }
1992   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1993   PetscCall(PetscDrawFlush(draw));
1994   PetscCall(PetscDrawPause(draw));
1995   PetscCall(PetscDrawSave(draw));
1996   PetscFunctionReturn(PETSC_SUCCESS);
1997 }
1998 
1999 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
2000 {
2001   DM           odm = dm, rdm = dm, cdm;
2002   PetscFE      fe;
2003   PetscSpace   sp;
2004   PetscClassId id;
2005   PetscInt     degree;
2006   PetscBool    hoView = PETSC_TRUE;
2007 
2008   PetscFunctionBegin;
2009   PetscObjectOptionsBegin((PetscObject)dm);
2010   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2011   PetscOptionsEnd();
2012   PetscCall(PetscObjectReference((PetscObject)dm));
2013   *hdm = dm;
2014   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2015   PetscCall(DMGetCoordinateDM(dm, &cdm));
2016   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2017   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2018   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2019   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2020   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2021   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2022     DM  cdm, rcdm;
2023     Mat In;
2024     Vec cl, rcl;
2025 
2026     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2027     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, PETSC_FALSE));
2028     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2029     PetscCall(DMGetCoordinateDM(odm, &cdm));
2030     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2031     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2032     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2033     PetscCall(DMSetCoarseDM(rcdm, cdm));
2034     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2035     PetscCall(MatMult(In, cl, rcl));
2036     PetscCall(MatDestroy(&In));
2037     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2038     PetscCall(DMDestroy(&odm));
2039     odm = rdm;
2040   }
2041   *hdm = rdm;
2042   PetscFunctionReturn(PETSC_SUCCESS);
2043 }
2044 
2045 #if defined(PETSC_HAVE_EXODUSII)
2046   #include <exodusII.h>
2047   #include <petscviewerexodusii.h>
2048 #endif
2049 
2050 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2051 {
2052   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2053   char      name[PETSC_MAX_PATH_LEN];
2054 
2055   PetscFunctionBegin;
2056   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2057   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2060   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2061   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2064   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2065   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2066   if (iascii) {
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2070     else PetscCall(DMPlexView_Ascii(dm, viewer));
2071   } else if (ishdf5) {
2072 #if defined(PETSC_HAVE_HDF5)
2073     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2074 #else
2075     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2076 #endif
2077   } else if (isvtk) {
2078     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2079   } else if (isdraw) {
2080     DM hdm;
2081 
2082     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2083     PetscCall(DMPlexView_Draw(hdm, viewer));
2084     PetscCall(DMDestroy(&hdm));
2085   } else if (isglvis) {
2086     PetscCall(DMPlexView_GLVis(dm, viewer));
2087 #if defined(PETSC_HAVE_EXODUSII)
2088   } else if (isexodus) {
2089     /*
2090       ExodusII requires that all sets be part of exactly one cell set.
2091       If the dm does not have a "Cell Sets" label defined, we create one
2092       with ID 1, containing all cells.
2093       Note that if the Cell Sets label is defined but does not cover all cells,
2094       we may still have a problem. This should probably be checked here or in the viewer;
2095     */
2096     PetscInt numCS;
2097     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2098     if (!numCS) {
2099       PetscInt cStart, cEnd, c;
2100       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2101       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2102       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2103     }
2104     PetscCall(DMView_PlexExodusII(dm, viewer));
2105 #endif
2106 #if defined(PETSC_HAVE_CGNS)
2107   } else if (iscgns) {
2108     PetscCall(DMView_PlexCGNS(dm, viewer));
2109 #endif
2110   } else if (ispython) {
2111     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2112   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2113   /* Optionally view the partition */
2114   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2115   if (flg) {
2116     Vec ranks;
2117     PetscCall(DMPlexCreateRankField(dm, &ranks));
2118     PetscCall(VecView(ranks, viewer));
2119     PetscCall(VecDestroy(&ranks));
2120   }
2121   /* Optionally view a label */
2122   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2123   if (flg) {
2124     DMLabel label;
2125     Vec     val;
2126 
2127     PetscCall(DMGetLabel(dm, name, &label));
2128     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2129     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2130     PetscCall(VecView(val, viewer));
2131     PetscCall(VecDestroy(&val));
2132   }
2133   PetscFunctionReturn(PETSC_SUCCESS);
2134 }
2135 
2136 /*@
2137   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2138 
2139   Collective
2140 
2141   Input Parameters:
2142 + dm     - The `DM` whose topology is to be saved
2143 - viewer - The `PetscViewer` to save it in
2144 
2145   Level: advanced
2146 
2147 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2148 @*/
2149 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2150 {
2151   PetscBool ishdf5;
2152 
2153   PetscFunctionBegin;
2154   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2155   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2156   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2157   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2158   if (ishdf5) {
2159 #if defined(PETSC_HAVE_HDF5)
2160     PetscViewerFormat format;
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2163       IS globalPointNumbering;
2164 
2165       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2166       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2167       PetscCall(ISDestroy(&globalPointNumbering));
2168     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2169 #else
2170     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2171 #endif
2172   }
2173   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2174   PetscFunctionReturn(PETSC_SUCCESS);
2175 }
2176 
2177 /*@
2178   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2179 
2180   Collective
2181 
2182   Input Parameters:
2183 + dm     - The `DM` whose coordinates are to be saved
2184 - viewer - The `PetscViewer` for saving
2185 
2186   Level: advanced
2187 
2188 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2189 @*/
2190 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2191 {
2192   PetscBool ishdf5;
2193 
2194   PetscFunctionBegin;
2195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2196   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2197   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2198   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2199   if (ishdf5) {
2200 #if defined(PETSC_HAVE_HDF5)
2201     PetscViewerFormat format;
2202     PetscCall(PetscViewerGetFormat(viewer, &format));
2203     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2204       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2205     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2206 #else
2207     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2208 #endif
2209   }
2210   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2211   PetscFunctionReturn(PETSC_SUCCESS);
2212 }
2213 
2214 /*@
2215   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2216 
2217   Collective
2218 
2219   Input Parameters:
2220 + dm     - The `DM` whose labels are to be saved
2221 - viewer - The `PetscViewer` for saving
2222 
2223   Level: advanced
2224 
2225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2226 @*/
2227 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2228 {
2229   PetscBool ishdf5;
2230 
2231   PetscFunctionBegin;
2232   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2233   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2234   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2235   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2236   if (ishdf5) {
2237 #if defined(PETSC_HAVE_HDF5)
2238     IS                globalPointNumbering;
2239     PetscViewerFormat format;
2240 
2241     PetscCall(PetscViewerGetFormat(viewer, &format));
2242     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2243       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2244       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2245       PetscCall(ISDestroy(&globalPointNumbering));
2246     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2247 #else
2248     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2249 #endif
2250   }
2251   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2252   PetscFunctionReturn(PETSC_SUCCESS);
2253 }
2254 
2255 /*@
2256   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2257 
2258   Collective
2259 
2260   Input Parameters:
2261 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2262 . viewer    - The `PetscViewer` for saving
2263 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2264 
2265   Level: advanced
2266 
2267   Notes:
2268   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.
2269 
2270   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.
2271 
2272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2273 @*/
2274 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2275 {
2276   PetscBool ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   if (!sectiondm) sectiondm = dm;
2282   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2288 #else
2289     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2290 #endif
2291   }
2292   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2293   PetscFunctionReturn(PETSC_SUCCESS);
2294 }
2295 
2296 /*@
2297   DMPlexGlobalVectorView - Saves a global vector
2298 
2299   Collective
2300 
2301   Input Parameters:
2302 + dm        - The `DM` that represents the topology
2303 . viewer    - The `PetscViewer` to save data with
2304 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2305 - vec       - The global vector to be saved
2306 
2307   Level: advanced
2308 
2309   Notes:
2310   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.
2311 
2312   Calling sequence:
2313 .vb
2314        DMCreate(PETSC_COMM_WORLD, &dm);
2315        DMSetType(dm, DMPLEX);
2316        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2317        DMClone(dm, &sectiondm);
2318        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2319        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2320        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2321        PetscSectionSetChart(section, pStart, pEnd);
2322        PetscSectionSetUp(section);
2323        DMSetLocalSection(sectiondm, section);
2324        PetscSectionDestroy(&section);
2325        DMGetGlobalVector(sectiondm, &vec);
2326        PetscObjectSetName((PetscObject)vec, "vec_name");
2327        DMPlexTopologyView(dm, viewer);
2328        DMPlexSectionView(dm, viewer, sectiondm);
2329        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2330        DMRestoreGlobalVector(sectiondm, &vec);
2331        DMDestroy(&sectiondm);
2332        DMDestroy(&dm);
2333 .ve
2334 
2335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2336 @*/
2337 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2338 {
2339   PetscBool ishdf5;
2340 
2341   PetscFunctionBegin;
2342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2344   if (!sectiondm) sectiondm = dm;
2345   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2346   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2347   /* Check consistency */
2348   {
2349     PetscSection section;
2350     PetscBool    includesConstraints;
2351     PetscInt     m, m1;
2352 
2353     PetscCall(VecGetLocalSize(vec, &m1));
2354     PetscCall(DMGetGlobalSection(sectiondm, &section));
2355     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2356     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2357     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2358     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2359   }
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2370   PetscFunctionReturn(PETSC_SUCCESS);
2371 }
2372 
2373 /*@
2374   DMPlexLocalVectorView - Saves a local vector
2375 
2376   Collective
2377 
2378   Input Parameters:
2379 + dm        - The `DM` that represents the topology
2380 . viewer    - The `PetscViewer` to save data with
2381 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2382 - vec       - The local vector to be saved
2383 
2384   Level: advanced
2385 
2386   Note:
2387   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.
2388 
2389   Calling sequence:
2390 .vb
2391        DMCreate(PETSC_COMM_WORLD, &dm);
2392        DMSetType(dm, DMPLEX);
2393        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2394        DMClone(dm, &sectiondm);
2395        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2396        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2397        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2398        PetscSectionSetChart(section, pStart, pEnd);
2399        PetscSectionSetUp(section);
2400        DMSetLocalSection(sectiondm, section);
2401        DMGetLocalVector(sectiondm, &vec);
2402        PetscObjectSetName((PetscObject)vec, "vec_name");
2403        DMPlexTopologyView(dm, viewer);
2404        DMPlexSectionView(dm, viewer, sectiondm);
2405        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2406        DMRestoreLocalVector(sectiondm, &vec);
2407        DMDestroy(&sectiondm);
2408        DMDestroy(&dm);
2409 .ve
2410 
2411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2412 @*/
2413 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2414 {
2415   PetscBool ishdf5;
2416 
2417   PetscFunctionBegin;
2418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2419   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2420   if (!sectiondm) sectiondm = dm;
2421   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2422   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2423   /* Check consistency */
2424   {
2425     PetscSection section;
2426     PetscBool    includesConstraints;
2427     PetscInt     m, m1;
2428 
2429     PetscCall(VecGetLocalSize(vec, &m1));
2430     PetscCall(DMGetLocalSection(sectiondm, &section));
2431     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2432     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2433     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2434     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2435   }
2436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2437   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2438   if (ishdf5) {
2439 #if defined(PETSC_HAVE_HDF5)
2440     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2441 #else
2442     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2443 #endif
2444   }
2445   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2446   PetscFunctionReturn(PETSC_SUCCESS);
2447 }
2448 
2449 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2450 {
2451   PetscBool ishdf5;
2452 
2453   PetscFunctionBegin;
2454   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2455   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2457   if (ishdf5) {
2458 #if defined(PETSC_HAVE_HDF5)
2459     PetscViewerFormat format;
2460     PetscCall(PetscViewerGetFormat(viewer, &format));
2461     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2462       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2463     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2464       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2465     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2466     PetscFunctionReturn(PETSC_SUCCESS);
2467 #else
2468     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2469 #endif
2470   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2471 }
2472 
2473 /*@
2474   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2475 
2476   Collective
2477 
2478   Input Parameters:
2479 + dm     - The `DM` into which the topology is loaded
2480 - viewer - The `PetscViewer` for the saved topology
2481 
2482   Output Parameter:
2483 . 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;
2484   `NULL` if unneeded
2485 
2486   Level: advanced
2487 
2488 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2489           `PetscViewer`, `PetscSF`
2490 @*/
2491 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2492 {
2493   PetscBool ishdf5;
2494 
2495   PetscFunctionBegin;
2496   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2497   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2498   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2499   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2500   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2501   if (ishdf5) {
2502 #if defined(PETSC_HAVE_HDF5)
2503     PetscViewerFormat format;
2504     PetscCall(PetscViewerGetFormat(viewer, &format));
2505     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2506       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2507     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2508 #else
2509     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2510 #endif
2511   }
2512   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2513   PetscFunctionReturn(PETSC_SUCCESS);
2514 }
2515 
2516 /*@
2517   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2518 
2519   Collective
2520 
2521   Input Parameters:
2522 + dm                   - The `DM` into which the coordinates are loaded
2523 . viewer               - The `PetscViewer` for the saved coordinates
2524 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2525 
2526   Level: advanced
2527 
2528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2529           `PetscSF`, `PetscViewer`
2530 @*/
2531 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2532 {
2533   PetscBool ishdf5;
2534 
2535   PetscFunctionBegin;
2536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2537   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2538   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2539   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2540   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2541   if (ishdf5) {
2542 #if defined(PETSC_HAVE_HDF5)
2543     PetscViewerFormat format;
2544     PetscCall(PetscViewerGetFormat(viewer, &format));
2545     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2546       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2547     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2548 #else
2549     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2550 #endif
2551   }
2552   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2553   PetscFunctionReturn(PETSC_SUCCESS);
2554 }
2555 
2556 /*@
2557   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2558 
2559   Collective
2560 
2561   Input Parameters:
2562 + dm                   - The `DM` into which the labels are loaded
2563 . viewer               - The `PetscViewer` for the saved labels
2564 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2565 
2566   Level: advanced
2567 
2568   Note:
2569   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2570 
2571 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2572           `PetscSF`, `PetscViewer`
2573 @*/
2574 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2575 {
2576   PetscBool ishdf5;
2577 
2578   PetscFunctionBegin;
2579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2580   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2581   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2583   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2584   if (ishdf5) {
2585 #if defined(PETSC_HAVE_HDF5)
2586     PetscViewerFormat format;
2587 
2588     PetscCall(PetscViewerGetFormat(viewer, &format));
2589     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2590       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2591     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2592 #else
2593     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2594 #endif
2595   }
2596   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2597   PetscFunctionReturn(PETSC_SUCCESS);
2598 }
2599 
2600 /*@
2601   DMPlexSectionLoad - Loads section into a `DMPLEX`
2602 
2603   Collective
2604 
2605   Input Parameters:
2606 + dm                   - The `DM` that represents the topology
2607 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2608 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2609 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2610 
2611   Output Parameters:
2612 + 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)
2613 - 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)
2614 
2615   Level: advanced
2616 
2617   Notes:
2618   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.
2619 
2620   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.
2621 
2622   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.
2623 
2624   Example using 2 processes:
2625 .vb
2626   NX (number of points on dm): 4
2627   sectionA                   : the on-disk section
2628   vecA                       : a vector associated with sectionA
2629   sectionB                   : sectiondm's local section constructed in this function
2630   vecB (local)               : a vector associated with sectiondm's local section
2631   vecB (global)              : a vector associated with sectiondm's global section
2632 
2633                                      rank 0    rank 1
2634   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2635   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2636   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2637   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2638   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2639   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2640   sectionB->atlasDof             :     1 0 1 | 1 3
2641   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2642   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2643   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2644 .ve
2645   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2646 
2647 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2648 @*/
2649 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2650 {
2651   PetscBool ishdf5;
2652 
2653   PetscFunctionBegin;
2654   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2655   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2656   if (!sectiondm) sectiondm = dm;
2657   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2658   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2659   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2660   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2661   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2662   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2663   if (ishdf5) {
2664 #if defined(PETSC_HAVE_HDF5)
2665     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2666 #else
2667     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2668 #endif
2669   }
2670   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2671   PetscFunctionReturn(PETSC_SUCCESS);
2672 }
2673 
2674 /*@
2675   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2676 
2677   Collective
2678 
2679   Input Parameters:
2680 + dm        - The `DM` that represents the topology
2681 . viewer    - The `PetscViewer` that represents the on-disk vector data
2682 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2683 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2684 - vec       - The global vector to set values of
2685 
2686   Level: advanced
2687 
2688   Notes:
2689   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.
2690 
2691   Calling sequence:
2692 .vb
2693        DMCreate(PETSC_COMM_WORLD, &dm);
2694        DMSetType(dm, DMPLEX);
2695        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2696        DMPlexTopologyLoad(dm, viewer, &sfX);
2697        DMClone(dm, &sectiondm);
2698        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2699        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2700        DMGetGlobalVector(sectiondm, &vec);
2701        PetscObjectSetName((PetscObject)vec, "vec_name");
2702        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2703        DMRestoreGlobalVector(sectiondm, &vec);
2704        PetscSFDestroy(&gsf);
2705        PetscSFDestroy(&sfX);
2706        DMDestroy(&sectiondm);
2707        DMDestroy(&dm);
2708 .ve
2709 
2710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2711           `PetscSF`, `PetscViewer`
2712 @*/
2713 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2714 {
2715   PetscBool ishdf5;
2716 
2717   PetscFunctionBegin;
2718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2719   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2720   if (!sectiondm) sectiondm = dm;
2721   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2722   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2723   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2724   /* Check consistency */
2725   {
2726     PetscSection section;
2727     PetscBool    includesConstraints;
2728     PetscInt     m, m1;
2729 
2730     PetscCall(VecGetLocalSize(vec, &m1));
2731     PetscCall(DMGetGlobalSection(sectiondm, &section));
2732     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2733     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2734     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2735     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2736   }
2737   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2738   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2739   if (ishdf5) {
2740 #if defined(PETSC_HAVE_HDF5)
2741     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2742 #else
2743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2744 #endif
2745   }
2746   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2747   PetscFunctionReturn(PETSC_SUCCESS);
2748 }
2749 
2750 /*@
2751   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2752 
2753   Collective
2754 
2755   Input Parameters:
2756 + dm        - The `DM` that represents the topology
2757 . viewer    - The `PetscViewer` that represents the on-disk vector data
2758 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2759 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2760 - vec       - The local vector to set values of
2761 
2762   Level: advanced
2763 
2764   Notes:
2765   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.
2766 
2767   Calling sequence:
2768 .vb
2769        DMCreate(PETSC_COMM_WORLD, &dm);
2770        DMSetType(dm, DMPLEX);
2771        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2772        DMPlexTopologyLoad(dm, viewer, &sfX);
2773        DMClone(dm, &sectiondm);
2774        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2775        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2776        DMGetLocalVector(sectiondm, &vec);
2777        PetscObjectSetName((PetscObject)vec, "vec_name");
2778        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2779        DMRestoreLocalVector(sectiondm, &vec);
2780        PetscSFDestroy(&lsf);
2781        PetscSFDestroy(&sfX);
2782        DMDestroy(&sectiondm);
2783        DMDestroy(&dm);
2784 .ve
2785 
2786 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2787           `PetscSF`, `PetscViewer`
2788 @*/
2789 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2790 {
2791   PetscBool ishdf5;
2792 
2793   PetscFunctionBegin;
2794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2795   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2796   if (!sectiondm) sectiondm = dm;
2797   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2798   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2799   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2800   /* Check consistency */
2801   {
2802     PetscSection section;
2803     PetscBool    includesConstraints;
2804     PetscInt     m, m1;
2805 
2806     PetscCall(VecGetLocalSize(vec, &m1));
2807     PetscCall(DMGetLocalSection(sectiondm, &section));
2808     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2809     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2810     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2811     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2812   }
2813   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2814   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2815   if (ishdf5) {
2816 #if defined(PETSC_HAVE_HDF5)
2817     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2818 #else
2819     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2820 #endif
2821   }
2822   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2823   PetscFunctionReturn(PETSC_SUCCESS);
2824 }
2825 
2826 PetscErrorCode DMDestroy_Plex(DM dm)
2827 {
2828   DM_Plex *mesh = (DM_Plex *)dm->data;
2829 
2830   PetscFunctionBegin;
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBounds_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2837   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2838   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2839   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2840   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2841   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2842   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2843   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2844   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2845   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2846   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2847   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2848   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2849   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2850   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2851   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2852   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2853   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2854   PetscCall(PetscFree(mesh->cones));
2855   PetscCall(PetscFree(mesh->coneOrientations));
2856   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2857   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2858   PetscCall(PetscFree(mesh->supports));
2859   PetscCall(PetscFree(mesh->cellTypes));
2860   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2861   PetscCall(PetscFree(mesh->tetgenOpts));
2862   PetscCall(PetscFree(mesh->triangleOpts));
2863   PetscCall(PetscFree(mesh->transformType));
2864   PetscCall(PetscFree(mesh->distributionName));
2865   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2866   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2867   PetscCall(ISDestroy(&mesh->subpointIS));
2868   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2869   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2870   if (mesh->periodic.face_sfs) {
2871     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2872     PetscCall(PetscFree(mesh->periodic.face_sfs));
2873   }
2874   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2875   if (mesh->periodic.periodic_points) {
2876     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2877     PetscCall(PetscFree(mesh->periodic.periodic_points));
2878   }
2879   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2880   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2881   PetscCall(ISDestroy(&mesh->anchorIS));
2882   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2883   PetscCall(PetscFree(mesh->parents));
2884   PetscCall(PetscFree(mesh->childIDs));
2885   PetscCall(PetscSectionDestroy(&mesh->childSection));
2886   PetscCall(PetscFree(mesh->children));
2887   PetscCall(DMDestroy(&mesh->referenceTree));
2888   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2889   PetscCall(PetscFree(mesh->neighbors));
2890   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2891   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2892   PetscCall(DMPlexTransformDestroy(&mesh->transform));
2893   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2894   PetscCall(PetscFree(mesh));
2895   PetscFunctionReturn(PETSC_SUCCESS);
2896 }
2897 
2898 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2899 {
2900   PetscSection           sectionGlobal, sectionLocal;
2901   PetscInt               bs = -1, mbs;
2902   PetscInt               localSize, localStart = 0;
2903   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2904   MatType                mtype;
2905   ISLocalToGlobalMapping ltog;
2906 
2907   PetscFunctionBegin;
2908   PetscCall(MatInitializePackage());
2909   mtype = dm->mattype;
2910   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2911   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2912   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2913   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2914   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2915   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2916   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2917   PetscCall(MatSetType(*J, mtype));
2918   PetscCall(MatSetFromOptions(*J));
2919   PetscCall(MatGetBlockSize(*J, &mbs));
2920   if (mbs > 1) bs = mbs;
2921   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2922   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2923   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2924   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2925   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2926   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2927   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2928   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2929   if (!isShell) {
2930     // There are three states with pblocks, since block starts can have no dofs:
2931     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2932     // TRUE)    Block Start: The first entry in a block has been added
2933     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2934     PetscBT         blst;
2935     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2936     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2937     const PetscInt *perm       = NULL;
2938     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2939     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2940 
2941     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2942     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2943     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2944 
2945     PetscCall(PetscCalloc1(localSize, &pblocks));
2946     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2947     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2948     // We need to process in the permuted order to get block sizes right
2949     for (PetscInt point = pStart; point < pEnd; ++point) {
2950       const PetscInt p = perm ? perm[point] : point;
2951 
2952       switch (dm->blocking_type) {
2953       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2954         PetscInt bdof, offset;
2955 
2956         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2957         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2958         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2959         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2960         if (dof > 0) {
2961           // State change
2962           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2963           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2964 
2965           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2966           // Signal block concatenation
2967           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2968         }
2969         dof  = dof < 0 ? -(dof + 1) : dof;
2970         bdof = cdof && (dof - cdof) ? 1 : dof;
2971         if (dof) {
2972           if (bs < 0) {
2973             bs = bdof;
2974           } else if (bs != bdof) {
2975             bs = 1;
2976           }
2977         }
2978       } break;
2979       case DM_BLOCKING_FIELD_NODE: {
2980         for (PetscInt field = 0; field < num_fields; field++) {
2981           PetscInt num_comp, bdof, offset;
2982           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2983           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2984           if (dof < 0) continue;
2985           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2986           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2987           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);
2988           PetscInt num_nodes = dof / num_comp;
2989           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2990           // Handle possibly constant block size (unlikely)
2991           bdof = cdof && (dof - cdof) ? 1 : dof;
2992           if (dof) {
2993             if (bs < 0) {
2994               bs = bdof;
2995             } else if (bs != bdof) {
2996               bs = 1;
2997             }
2998           }
2999         }
3000       } break;
3001       }
3002     }
3003     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
3004     /* Must have same blocksize on all procs (some might have no points) */
3005     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
3006     bsLocal[1] = bs;
3007     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
3008     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
3009     else bs = bsMinMax[0];
3010     bs = PetscMax(1, bs);
3011     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
3012     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
3013       PetscCall(MatSetBlockSize(*J, bs));
3014       PetscCall(MatSetUp(*J));
3015     } else {
3016       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3017       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3018       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3019     }
3020     if (pblocks) { // Consolidate blocks
3021       PetscInt nblocks = 0;
3022       pblocks[0]       = PetscAbs(pblocks[0]);
3023       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3024         if (pblocks[i] == 0) continue;
3025         // Negative block size indicates the blocks should be concatenated
3026         if (pblocks[i] < 0) {
3027           pblocks[i] = -pblocks[i];
3028           pblocks[nblocks - 1] += pblocks[i];
3029         } else {
3030           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3031         }
3032         for (PetscInt j = 1; j < pblocks[i]; j++)
3033           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);
3034       }
3035       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3036     }
3037     PetscCall(PetscFree(pblocks));
3038   }
3039   PetscCall(MatSetDM(*J, dm));
3040   PetscFunctionReturn(PETSC_SUCCESS);
3041 }
3042 
3043 /*@
3044   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3045 
3046   Not Collective
3047 
3048   Input Parameter:
3049 . dm - The `DMPLEX`
3050 
3051   Output Parameter:
3052 . subsection - The subdomain section
3053 
3054   Level: developer
3055 
3056 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3057 @*/
3058 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3059 {
3060   DM_Plex *mesh = (DM_Plex *)dm->data;
3061 
3062   PetscFunctionBegin;
3063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3064   if (!mesh->subdomainSection) {
3065     PetscSection section;
3066     PetscSF      sf;
3067 
3068     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3069     PetscCall(DMGetLocalSection(dm, &section));
3070     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3071     PetscCall(PetscSFDestroy(&sf));
3072   }
3073   *subsection = mesh->subdomainSection;
3074   PetscFunctionReturn(PETSC_SUCCESS);
3075 }
3076 
3077 /*@
3078   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3079 
3080   Not Collective
3081 
3082   Input Parameter:
3083 . dm - The `DMPLEX`
3084 
3085   Output Parameters:
3086 + pStart - The first mesh point
3087 - pEnd   - The upper bound for mesh points
3088 
3089   Level: beginner
3090 
3091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3092 @*/
3093 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3094 {
3095   DM_Plex *mesh = (DM_Plex *)dm->data;
3096 
3097   PetscFunctionBegin;
3098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3099   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3100   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3101   PetscFunctionReturn(PETSC_SUCCESS);
3102 }
3103 
3104 /*@
3105   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3106 
3107   Not Collective
3108 
3109   Input Parameters:
3110 + dm     - The `DMPLEX`
3111 . pStart - The first mesh point
3112 - pEnd   - The upper bound for mesh points
3113 
3114   Level: beginner
3115 
3116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3117 @*/
3118 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3119 {
3120   DM_Plex *mesh = (DM_Plex *)dm->data;
3121 
3122   PetscFunctionBegin;
3123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3124   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3125   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3126   PetscCall(PetscFree(mesh->cellTypes));
3127   PetscFunctionReturn(PETSC_SUCCESS);
3128 }
3129 
3130 /*@
3131   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3132 
3133   Not Collective
3134 
3135   Input Parameters:
3136 + dm - The `DMPLEX`
3137 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3138 
3139   Output Parameter:
3140 . size - The cone size for point `p`
3141 
3142   Level: beginner
3143 
3144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3145 @*/
3146 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3147 {
3148   DM_Plex *mesh = (DM_Plex *)dm->data;
3149 
3150   PetscFunctionBegin;
3151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3152   PetscAssertPointer(size, 3);
3153   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3154   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3155   PetscFunctionReturn(PETSC_SUCCESS);
3156 }
3157 
3158 /*@
3159   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3160 
3161   Not Collective
3162 
3163   Input Parameters:
3164 + dm   - The `DMPLEX`
3165 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3166 - size - The cone size for point `p`
3167 
3168   Level: beginner
3169 
3170   Note:
3171   This should be called after `DMPlexSetChart()`.
3172 
3173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3174 @*/
3175 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3176 {
3177   DM_Plex *mesh = (DM_Plex *)dm->data;
3178 
3179   PetscFunctionBegin;
3180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3181   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3182   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3183   PetscFunctionReturn(PETSC_SUCCESS);
3184 }
3185 
3186 /*@C
3187   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3188 
3189   Not Collective
3190 
3191   Input Parameters:
3192 + dm - The `DMPLEX`
3193 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3194 
3195   Output Parameter:
3196 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3197 
3198   Level: beginner
3199 
3200   Fortran Notes:
3201   `cone` must be declared with
3202 .vb
3203   PetscInt, pointer :: cone(:)
3204 .ve
3205 
3206   You must call `DMPlexRestoreCone()` after you finish using the array.
3207   `DMPlexRestoreCone()` is not needed/available in C.
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3210 @*/
3211 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt off;
3215 
3216   PetscFunctionBegin;
3217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3218   PetscAssertPointer(cone, 3);
3219   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3220   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3221   PetscFunctionReturn(PETSC_SUCCESS);
3222 }
3223 
3224 /*@
3225   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3226 
3227   Not Collective
3228 
3229   Input Parameters:
3230 + dm - The `DMPLEX`
3231 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3232 
3233   Output Parameters:
3234 + pConesSection - `PetscSection` describing the layout of `pCones`
3235 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3236 
3237   Level: intermediate
3238 
3239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3240 @*/
3241 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3242 {
3243   PetscSection cs, newcs;
3244   PetscInt    *cones;
3245   PetscInt    *newarr = NULL;
3246   PetscInt     n;
3247 
3248   PetscFunctionBegin;
3249   PetscCall(DMPlexGetCones(dm, &cones));
3250   PetscCall(DMPlexGetConeSection(dm, &cs));
3251   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3252   if (pConesSection) *pConesSection = newcs;
3253   if (pCones) {
3254     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3255     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3256   }
3257   PetscFunctionReturn(PETSC_SUCCESS);
3258 }
3259 
3260 /*@
3261   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3262 
3263   Not Collective
3264 
3265   Input Parameters:
3266 + dm     - The `DMPLEX`
3267 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3268 
3269   Output Parameter:
3270 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3271 
3272   Level: advanced
3273 
3274   Notes:
3275   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3276 
3277   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3278 
3279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3280           `DMPlexGetDepth()`, `IS`
3281 @*/
3282 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3283 {
3284   IS      *expandedPointsAll;
3285   PetscInt depth;
3286 
3287   PetscFunctionBegin;
3288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3289   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3290   PetscAssertPointer(expandedPoints, 3);
3291   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3292   *expandedPoints = expandedPointsAll[0];
3293   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3294   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3295   PetscFunctionReturn(PETSC_SUCCESS);
3296 }
3297 
3298 /*@
3299   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3300   (DAG points of depth 0, i.e., without cones).
3301 
3302   Not Collective
3303 
3304   Input Parameters:
3305 + dm     - The `DMPLEX`
3306 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3307 
3308   Output Parameters:
3309 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3310 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3311 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3312 
3313   Level: advanced
3314 
3315   Notes:
3316   Like `DMPlexGetConeTuple()` but recursive.
3317 
3318   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.
3319   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3320 
3321   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\:
3322   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3323   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3324 
3325 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3326           `DMPlexGetDepth()`, `PetscSection`, `IS`
3327 @*/
3328 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3329 {
3330   const PetscInt *arr0 = NULL, *cone = NULL;
3331   PetscInt       *arr = NULL, *newarr = NULL;
3332   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3333   IS             *expandedPoints_;
3334   PetscSection   *sections_;
3335 
3336   PetscFunctionBegin;
3337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3338   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3339   if (depth) PetscAssertPointer(depth, 3);
3340   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3341   if (sections) PetscAssertPointer(sections, 5);
3342   PetscCall(ISGetLocalSize(points, &n));
3343   PetscCall(ISGetIndices(points, &arr0));
3344   PetscCall(DMPlexGetDepth(dm, &depth_));
3345   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3346   PetscCall(PetscCalloc1(depth_, &sections_));
3347   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3348   for (d = depth_ - 1; d >= 0; d--) {
3349     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3350     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3351     for (i = 0; i < n; i++) {
3352       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3353       if (arr[i] >= start && arr[i] < end) {
3354         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3355         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3356       } else {
3357         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3358       }
3359     }
3360     PetscCall(PetscSectionSetUp(sections_[d]));
3361     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3362     PetscCall(PetscMalloc1(newn, &newarr));
3363     for (i = 0; i < n; i++) {
3364       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3365       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3366       if (cn > 1) {
3367         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3368         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3369       } else {
3370         newarr[co] = arr[i];
3371       }
3372     }
3373     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3374     arr = newarr;
3375     n   = newn;
3376   }
3377   PetscCall(ISRestoreIndices(points, &arr0));
3378   *depth = depth_;
3379   if (expandedPoints) *expandedPoints = expandedPoints_;
3380   else {
3381     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3382     PetscCall(PetscFree(expandedPoints_));
3383   }
3384   if (sections) *sections = sections_;
3385   else {
3386     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3387     PetscCall(PetscFree(sections_));
3388   }
3389   PetscFunctionReturn(PETSC_SUCCESS);
3390 }
3391 
3392 /*@
3393   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3394 
3395   Not Collective
3396 
3397   Input Parameters:
3398 + dm     - The `DMPLEX`
3399 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3400 
3401   Output Parameters:
3402 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3403 . expandedPoints - (optional) An array of recursively expanded cones
3404 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3405 
3406   Level: advanced
3407 
3408   Note:
3409   See `DMPlexGetConeRecursive()`
3410 
3411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3412           `DMPlexGetDepth()`, `IS`, `PetscSection`
3413 @*/
3414 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3415 {
3416   PetscInt d, depth_;
3417 
3418   PetscFunctionBegin;
3419   PetscCall(DMPlexGetDepth(dm, &depth_));
3420   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3421   if (depth) *depth = 0;
3422   if (expandedPoints) {
3423     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3424     PetscCall(PetscFree(*expandedPoints));
3425   }
3426   if (sections) {
3427     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3428     PetscCall(PetscFree(*sections));
3429   }
3430   PetscFunctionReturn(PETSC_SUCCESS);
3431 }
3432 
3433 /*@
3434   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
3435 
3436   Not Collective
3437 
3438   Input Parameters:
3439 + dm   - The `DMPLEX`
3440 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3441 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3442 
3443   Level: beginner
3444 
3445   Note:
3446   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3447 
3448 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3449 @*/
3450 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3451 {
3452   DM_Plex *mesh = (DM_Plex *)dm->data;
3453   PetscInt dof, off, c;
3454 
3455   PetscFunctionBegin;
3456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3457   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3458   if (dof) PetscAssertPointer(cone, 3);
3459   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3460   if (PetscDefined(USE_DEBUG)) {
3461     PetscInt pStart, pEnd;
3462     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3463     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);
3464     for (c = 0; c < dof; ++c) {
3465       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);
3466       mesh->cones[off + c] = cone[c];
3467     }
3468   } else {
3469     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3470   }
3471   PetscFunctionReturn(PETSC_SUCCESS);
3472 }
3473 
3474 /*@C
3475   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3476 
3477   Not Collective
3478 
3479   Input Parameters:
3480 + dm - The `DMPLEX`
3481 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3482 
3483   Output Parameter:
3484 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3485                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3486 
3487   Level: beginner
3488 
3489   Note:
3490   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3491   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3492   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3493   with the identity.
3494 
3495   Fortran Notes:
3496   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3497   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3498 
3499 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3500           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3501 @*/
3502 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3503 {
3504   DM_Plex *mesh = (DM_Plex *)dm->data;
3505   PetscInt off;
3506 
3507   PetscFunctionBegin;
3508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3509   if (PetscDefined(USE_DEBUG)) {
3510     PetscInt dof;
3511     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3512     if (dof) PetscAssertPointer(coneOrientation, 3);
3513   }
3514   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3515 
3516   *coneOrientation = &mesh->coneOrientations[off];
3517   PetscFunctionReturn(PETSC_SUCCESS);
3518 }
3519 
3520 /*@
3521   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3522 
3523   Not Collective
3524 
3525   Input Parameters:
3526 + dm              - The `DMPLEX`
3527 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3528 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3529 
3530   Level: beginner
3531 
3532   Notes:
3533   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3534 
3535   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3536 
3537 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3538 @*/
3539 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3540 {
3541   DM_Plex *mesh = (DM_Plex *)dm->data;
3542   PetscInt pStart, pEnd;
3543   PetscInt dof, off, c;
3544 
3545   PetscFunctionBegin;
3546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3547   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3548   if (dof) PetscAssertPointer(coneOrientation, 3);
3549   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3550   if (PetscDefined(USE_DEBUG)) {
3551     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3552     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);
3553     for (c = 0; c < dof; ++c) {
3554       PetscInt cdof, o = coneOrientation[c];
3555 
3556       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3557       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);
3558       mesh->coneOrientations[off + c] = o;
3559     }
3560   } else {
3561     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3562   }
3563   PetscFunctionReturn(PETSC_SUCCESS);
3564 }
3565 
3566 /*@
3567   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3568 
3569   Not Collective
3570 
3571   Input Parameters:
3572 + dm        - The `DMPLEX`
3573 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3574 . conePos   - The local index in the cone where the point should be put
3575 - conePoint - The mesh point to insert
3576 
3577   Level: beginner
3578 
3579 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3580 @*/
3581 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3582 {
3583   DM_Plex *mesh = (DM_Plex *)dm->data;
3584   PetscInt pStart, pEnd;
3585   PetscInt dof, off;
3586 
3587   PetscFunctionBegin;
3588   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3589   if (PetscDefined(USE_DEBUG)) {
3590     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3591     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);
3592     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);
3593     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3594     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);
3595   }
3596   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3597   mesh->cones[off + conePos] = conePoint;
3598   PetscFunctionReturn(PETSC_SUCCESS);
3599 }
3600 
3601 /*@
3602   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3603 
3604   Not Collective
3605 
3606   Input Parameters:
3607 + dm              - The `DMPLEX`
3608 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3609 . conePos         - The local index in the cone where the point should be put
3610 - coneOrientation - The point orientation to insert
3611 
3612   Level: beginner
3613 
3614   Note:
3615   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3616 
3617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3618 @*/
3619 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3620 {
3621   DM_Plex *mesh = (DM_Plex *)dm->data;
3622   PetscInt pStart, pEnd;
3623   PetscInt dof, off;
3624 
3625   PetscFunctionBegin;
3626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3627   if (PetscDefined(USE_DEBUG)) {
3628     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3629     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);
3630     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3631     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);
3632   }
3633   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3634   mesh->coneOrientations[off + conePos] = coneOrientation;
3635   PetscFunctionReturn(PETSC_SUCCESS);
3636 }
3637 
3638 /*@C
3639   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3640 
3641   Not collective
3642 
3643   Input Parameters:
3644 + dm - The DMPlex
3645 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3646 
3647   Output Parameters:
3648 + cone - An array of points which are on the in-edges for point `p`
3649 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3650          integer giving the prescription for cone traversal.
3651 
3652   Level: beginner
3653 
3654   Notes:
3655   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3656   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3657   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3658   with the identity.
3659 
3660   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3661 
3662   Fortran Notes:
3663   `cone` and `ornt` must be declared with
3664 .vb
3665   PetscInt, pointer :: cone(:)
3666   PetscInt, pointer :: ornt(:)
3667 .ve
3668 
3669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3670 @*/
3671 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3672 {
3673   DM_Plex *mesh = (DM_Plex *)dm->data;
3674 
3675   PetscFunctionBegin;
3676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3677   if (mesh->tr) {
3678     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3679   } else {
3680     PetscInt off;
3681     if (PetscDefined(USE_DEBUG)) {
3682       PetscInt dof;
3683       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3684       if (dof) {
3685         if (cone) PetscAssertPointer(cone, 3);
3686         if (ornt) PetscAssertPointer(ornt, 4);
3687       }
3688     }
3689     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3690     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3691     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3692   }
3693   PetscFunctionReturn(PETSC_SUCCESS);
3694 }
3695 
3696 /*@C
3697   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3698 
3699   Not Collective
3700 
3701   Input Parameters:
3702 + dm   - The DMPlex
3703 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3704 . cone - An array of points which are on the in-edges for point p
3705 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3706          integer giving the prescription for cone traversal.
3707 
3708   Level: beginner
3709 
3710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3711 @*/
3712 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3713 {
3714   DM_Plex *mesh = (DM_Plex *)dm->data;
3715 
3716   PetscFunctionBegin;
3717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3718   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3719   PetscFunctionReturn(PETSC_SUCCESS);
3720 }
3721 
3722 /*@
3723   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3724 
3725   Not Collective
3726 
3727   Input Parameters:
3728 + dm - The `DMPLEX`
3729 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3730 
3731   Output Parameter:
3732 . size - The support size for point `p`
3733 
3734   Level: beginner
3735 
3736 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3737 @*/
3738 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3739 {
3740   DM_Plex *mesh = (DM_Plex *)dm->data;
3741 
3742   PetscFunctionBegin;
3743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3744   PetscAssertPointer(size, 3);
3745   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3746   PetscFunctionReturn(PETSC_SUCCESS);
3747 }
3748 
3749 /*@
3750   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3751 
3752   Not Collective
3753 
3754   Input Parameters:
3755 + dm   - The `DMPLEX`
3756 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3757 - size - The support size for point `p`
3758 
3759   Level: beginner
3760 
3761   Note:
3762   This should be called after `DMPlexSetChart()`.
3763 
3764 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3765 @*/
3766 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3767 {
3768   DM_Plex *mesh = (DM_Plex *)dm->data;
3769 
3770   PetscFunctionBegin;
3771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3772   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3773   PetscFunctionReturn(PETSC_SUCCESS);
3774 }
3775 
3776 /*@C
3777   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3778 
3779   Not Collective
3780 
3781   Input Parameters:
3782 + dm - The `DMPLEX`
3783 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3784 
3785   Output Parameter:
3786 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3787 
3788   Level: beginner
3789 
3790   Fortran Notes:
3791   `support` must be declared with
3792 .vb
3793   PetscInt, pointer :: support(:)
3794 .ve
3795 
3796   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3797   `DMPlexRestoreSupport()` is not needed/available in C.
3798 
3799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3800 @*/
3801 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3802 {
3803   DM_Plex *mesh = (DM_Plex *)dm->data;
3804   PetscInt off;
3805 
3806   PetscFunctionBegin;
3807   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3808   PetscAssertPointer(support, 3);
3809   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3810   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3811   PetscFunctionReturn(PETSC_SUCCESS);
3812 }
3813 
3814 /*@
3815   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3816 
3817   Not Collective
3818 
3819   Input Parameters:
3820 + dm      - The `DMPLEX`
3821 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3822 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3823 
3824   Level: beginner
3825 
3826   Note:
3827   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3828 
3829 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3830 @*/
3831 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3832 {
3833   DM_Plex *mesh = (DM_Plex *)dm->data;
3834   PetscInt pStart, pEnd;
3835   PetscInt dof, off, c;
3836 
3837   PetscFunctionBegin;
3838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3839   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3840   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3841   if (dof) PetscAssertPointer(support, 3);
3842   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3843   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);
3844   for (c = 0; c < dof; ++c) {
3845     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);
3846     mesh->supports[off + c] = support[c];
3847   }
3848   PetscFunctionReturn(PETSC_SUCCESS);
3849 }
3850 
3851 /*@
3852   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3853 
3854   Not Collective
3855 
3856   Input Parameters:
3857 + dm           - The `DMPLEX`
3858 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3859 . supportPos   - The local index in the cone where the point should be put
3860 - supportPoint - The mesh point to insert
3861 
3862   Level: beginner
3863 
3864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3865 @*/
3866 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3867 {
3868   DM_Plex *mesh = (DM_Plex *)dm->data;
3869   PetscInt pStart, pEnd;
3870   PetscInt dof, off;
3871 
3872   PetscFunctionBegin;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3875   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3876   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3877   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);
3878   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);
3879   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);
3880   mesh->supports[off + supportPos] = supportPoint;
3881   PetscFunctionReturn(PETSC_SUCCESS);
3882 }
3883 
3884 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3885 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3886 {
3887   switch (ct) {
3888   case DM_POLYTOPE_SEGMENT:
3889     if (o == -1) return -2;
3890     break;
3891   case DM_POLYTOPE_TRIANGLE:
3892     if (o == -3) return -1;
3893     if (o == -2) return -3;
3894     if (o == -1) return -2;
3895     break;
3896   case DM_POLYTOPE_QUADRILATERAL:
3897     if (o == -4) return -2;
3898     if (o == -3) return -1;
3899     if (o == -2) return -4;
3900     if (o == -1) return -3;
3901     break;
3902   default:
3903     return o;
3904   }
3905   return o;
3906 }
3907 
3908 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3909 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3910 {
3911   switch (ct) {
3912   case DM_POLYTOPE_SEGMENT:
3913     if ((o == -2) || (o == 1)) return -1;
3914     if (o == -1) return 0;
3915     break;
3916   case DM_POLYTOPE_TRIANGLE:
3917     if (o == -3) return -2;
3918     if (o == -2) return -1;
3919     if (o == -1) return -3;
3920     break;
3921   case DM_POLYTOPE_QUADRILATERAL:
3922     if (o == -4) return -2;
3923     if (o == -3) return -1;
3924     if (o == -2) return -4;
3925     if (o == -1) return -3;
3926     break;
3927   default:
3928     return o;
3929   }
3930   return o;
3931 }
3932 
3933 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3934 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3935 {
3936   PetscInt pStart, pEnd, p;
3937 
3938   PetscFunctionBegin;
3939   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3940   for (p = pStart; p < pEnd; ++p) {
3941     const PetscInt *cone, *ornt;
3942     PetscInt        coneSize, c;
3943 
3944     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3945     PetscCall(DMPlexGetCone(dm, p, &cone));
3946     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3947     for (c = 0; c < coneSize; ++c) {
3948       DMPolytopeType ct;
3949       const PetscInt o = ornt[c];
3950 
3951       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3952       switch (ct) {
3953       case DM_POLYTOPE_SEGMENT:
3954         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3955         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3956         break;
3957       case DM_POLYTOPE_TRIANGLE:
3958         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3959         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3960         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3961         break;
3962       case DM_POLYTOPE_QUADRILATERAL:
3963         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3964         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3965         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3966         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3967         break;
3968       default:
3969         break;
3970       }
3971     }
3972   }
3973   PetscFunctionReturn(PETSC_SUCCESS);
3974 }
3975 
3976 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3977 {
3978   DM_Plex *mesh = (DM_Plex *)dm->data;
3979 
3980   PetscFunctionBeginHot;
3981   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3982     if (useCone) {
3983       PetscCall(DMPlexGetConeSize(dm, p, size));
3984       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3985     } else {
3986       PetscCall(DMPlexGetSupportSize(dm, p, size));
3987       PetscCall(DMPlexGetSupport(dm, p, arr));
3988     }
3989   } else {
3990     if (useCone) {
3991       const PetscSection s   = mesh->coneSection;
3992       const PetscInt     ps  = p - s->pStart;
3993       const PetscInt     off = s->atlasOff[ps];
3994 
3995       *size = s->atlasDof[ps];
3996       *arr  = mesh->cones + off;
3997       *ornt = mesh->coneOrientations + off;
3998     } else {
3999       const PetscSection s   = mesh->supportSection;
4000       const PetscInt     ps  = p - s->pStart;
4001       const PetscInt     off = s->atlasOff[ps];
4002 
4003       *size = s->atlasDof[ps];
4004       *arr  = mesh->supports + off;
4005     }
4006   }
4007   PetscFunctionReturn(PETSC_SUCCESS);
4008 }
4009 
4010 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
4011 {
4012   DM_Plex *mesh = (DM_Plex *)dm->data;
4013 
4014   PetscFunctionBeginHot;
4015   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4016     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4017   }
4018   PetscFunctionReturn(PETSC_SUCCESS);
4019 }
4020 
4021 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4022 {
4023   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4024   PetscInt       *closure;
4025   const PetscInt *tmp = NULL, *tmpO = NULL;
4026   PetscInt        off = 0, tmpSize, t;
4027 
4028   PetscFunctionBeginHot;
4029   if (ornt) {
4030     PetscCall(DMPlexGetCellType(dm, p, &ct));
4031     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;
4032   }
4033   if (*points) {
4034     closure = *points;
4035   } else {
4036     PetscInt maxConeSize, maxSupportSize;
4037     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4038     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4039   }
4040   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4041   if (ct == DM_POLYTOPE_UNKNOWN) {
4042     closure[off++] = p;
4043     closure[off++] = 0;
4044     for (t = 0; t < tmpSize; ++t) {
4045       closure[off++] = tmp[t];
4046       closure[off++] = tmpO ? tmpO[t] : 0;
4047     }
4048   } else {
4049     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4050 
4051     /* We assume that cells with a valid type have faces with a valid type */
4052     closure[off++] = p;
4053     closure[off++] = ornt;
4054     for (t = 0; t < tmpSize; ++t) {
4055       DMPolytopeType ft;
4056 
4057       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4058       closure[off++] = tmp[arr[t]];
4059       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4060     }
4061   }
4062   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4063   if (numPoints) *numPoints = tmpSize + 1;
4064   if (points) *points = closure;
4065   PetscFunctionReturn(PETSC_SUCCESS);
4066 }
4067 
4068 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4069 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4070 {
4071   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4072   const PetscInt *cone, *ornt;
4073   PetscInt       *pts, *closure = NULL;
4074   DMPolytopeType  ft;
4075   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4076   PetscInt        dim, coneSize, c, d, clSize, cl;
4077 
4078   PetscFunctionBeginHot;
4079   PetscCall(DMGetDimension(dm, &dim));
4080   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4081   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4082   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4083   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4084   maxSize       = PetscMax(coneSeries, supportSeries);
4085   if (*points) {
4086     pts = *points;
4087   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4088   c        = 0;
4089   pts[c++] = point;
4090   pts[c++] = o;
4091   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4092   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4093   for (cl = 0; cl < clSize * 2; cl += 2) {
4094     pts[c++] = closure[cl];
4095     pts[c++] = closure[cl + 1];
4096   }
4097   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4098   for (cl = 0; cl < clSize * 2; cl += 2) {
4099     pts[c++] = closure[cl];
4100     pts[c++] = closure[cl + 1];
4101   }
4102   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4103   for (d = 2; d < coneSize; ++d) {
4104     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4105     pts[c++] = cone[arr[d * 2 + 0]];
4106     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4107   }
4108   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4109   if (dim >= 3) {
4110     for (d = 2; d < coneSize; ++d) {
4111       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4112       const PetscInt *fcone, *fornt;
4113       PetscInt        fconeSize, fc, i;
4114 
4115       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4116       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4117       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4118       for (fc = 0; fc < fconeSize; ++fc) {
4119         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4120         const PetscInt co = farr[fc * 2 + 1];
4121 
4122         for (i = 0; i < c; i += 2)
4123           if (pts[i] == cp) break;
4124         if (i == c) {
4125           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4126           pts[c++] = cp;
4127           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4128         }
4129       }
4130       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4131     }
4132   }
4133   *numPoints = c / 2;
4134   *points    = pts;
4135   PetscFunctionReturn(PETSC_SUCCESS);
4136 }
4137 
4138 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4139 {
4140   DMPolytopeType ct;
4141   PetscInt      *closure, *fifo;
4142   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4143   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4144   PetscInt       depth, maxSize;
4145 
4146   PetscFunctionBeginHot;
4147   PetscCall(DMPlexGetDepth(dm, &depth));
4148   if (depth == 1) {
4149     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4150     PetscFunctionReturn(PETSC_SUCCESS);
4151   }
4152   PetscCall(DMPlexGetCellType(dm, p, &ct));
4153   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;
4154   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4155     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4156     PetscFunctionReturn(PETSC_SUCCESS);
4157   }
4158   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4159   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4160   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4161   maxSize       = PetscMax(coneSeries, supportSeries);
4162   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4163   if (*points) {
4164     closure = *points;
4165   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4166   closure[closureSize++] = p;
4167   closure[closureSize++] = ornt;
4168   fifo[fifoSize++]       = p;
4169   fifo[fifoSize++]       = ornt;
4170   fifo[fifoSize++]       = ct;
4171   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4172   while (fifoSize - fifoStart) {
4173     const PetscInt       q    = fifo[fifoStart++];
4174     const PetscInt       o    = fifo[fifoStart++];
4175     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4176     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4177     const PetscInt      *tmp, *tmpO = NULL;
4178     PetscInt             tmpSize, t;
4179 
4180     if (PetscDefined(USE_DEBUG)) {
4181       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4182       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);
4183     }
4184     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4185     for (t = 0; t < tmpSize; ++t) {
4186       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4187       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4188       const PetscInt cp = tmp[ip];
4189       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4190       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4191       PetscInt       c;
4192 
4193       /* Check for duplicate */
4194       for (c = 0; c < closureSize; c += 2) {
4195         if (closure[c] == cp) break;
4196       }
4197       if (c == closureSize) {
4198         closure[closureSize++] = cp;
4199         closure[closureSize++] = co;
4200         fifo[fifoSize++]       = cp;
4201         fifo[fifoSize++]       = co;
4202         fifo[fifoSize++]       = ct;
4203       }
4204     }
4205     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4206   }
4207   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4208   if (numPoints) *numPoints = closureSize / 2;
4209   if (points) *points = closure;
4210   PetscFunctionReturn(PETSC_SUCCESS);
4211 }
4212 
4213 /*@C
4214   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4215 
4216   Not Collective
4217 
4218   Input Parameters:
4219 + dm      - The `DMPLEX`
4220 . p       - The mesh point
4221 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4222 
4223   Input/Output Parameter:
4224 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4225            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4226            otherwise the provided array is used to hold the values
4227 
4228   Output Parameter:
4229 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4230 
4231   Level: beginner
4232 
4233   Note:
4234   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4235 
4236   Fortran Notes:
4237   `points` must be declared with
4238 .vb
4239   PetscInt, pointer :: points(:)
4240 .ve
4241   and is always allocated by the function.
4242 
4243   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4244 
4245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4246 @*/
4247 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4248 {
4249   PetscFunctionBeginHot;
4250   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4251   if (numPoints) PetscAssertPointer(numPoints, 4);
4252   if (points) PetscAssertPointer(points, 5);
4253   if (PetscDefined(USE_DEBUG)) {
4254     PetscInt pStart, pEnd;
4255     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4256     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);
4257   }
4258   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4259   PetscFunctionReturn(PETSC_SUCCESS);
4260 }
4261 
4262 /*@C
4263   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4264 
4265   Not Collective
4266 
4267   Input Parameters:
4268 + dm        - The `DMPLEX`
4269 . p         - The mesh point
4270 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4271 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4272 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4273 
4274   Level: beginner
4275 
4276   Note:
4277   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4278 
4279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4280 @*/
4281 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4282 {
4283   PetscFunctionBeginHot;
4284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4285   if (numPoints) *numPoints = 0;
4286   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4287   PetscFunctionReturn(PETSC_SUCCESS);
4288 }
4289 
4290 /*@
4291   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4292 
4293   Not Collective
4294 
4295   Input Parameter:
4296 . dm - The `DMPLEX`
4297 
4298   Output Parameters:
4299 + maxConeSize    - The maximum number of in-edges
4300 - maxSupportSize - The maximum number of out-edges
4301 
4302   Level: beginner
4303 
4304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4305 @*/
4306 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4307 {
4308   DM_Plex *mesh = (DM_Plex *)dm->data;
4309 
4310   PetscFunctionBegin;
4311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4312   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4313   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4314   PetscFunctionReturn(PETSC_SUCCESS);
4315 }
4316 
4317 PetscErrorCode DMSetUp_Plex(DM dm)
4318 {
4319   DM_Plex *mesh = (DM_Plex *)dm->data;
4320   PetscInt size, maxSupportSize;
4321 
4322   PetscFunctionBegin;
4323   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4324   PetscCall(PetscSectionSetUp(mesh->coneSection));
4325   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4326   PetscCall(PetscMalloc1(size, &mesh->cones));
4327   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4328   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4329   if (maxSupportSize) {
4330     PetscCall(PetscSectionSetUp(mesh->supportSection));
4331     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4332     PetscCall(PetscMalloc1(size, &mesh->supports));
4333   }
4334   PetscFunctionReturn(PETSC_SUCCESS);
4335 }
4336 
4337 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4338 {
4339   PetscFunctionBegin;
4340   if (subdm) PetscCall(DMClone(dm, subdm));
4341   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4342   if (subdm) (*subdm)->useNatural = dm->useNatural;
4343   if (dm->useNatural && dm->sfMigration) {
4344     PetscSF sfNatural;
4345 
4346     (*subdm)->sfMigration = dm->sfMigration;
4347     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4348     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4349     (*subdm)->sfNatural = sfNatural;
4350   }
4351   PetscFunctionReturn(PETSC_SUCCESS);
4352 }
4353 
4354 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4355 {
4356   PetscInt i = 0;
4357 
4358   PetscFunctionBegin;
4359   PetscCall(DMClone(dms[0], superdm));
4360   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4361   (*superdm)->useNatural = PETSC_FALSE;
4362   for (i = 0; i < len; i++) {
4363     if (dms[i]->useNatural && dms[i]->sfMigration) {
4364       PetscSF sfNatural;
4365 
4366       (*superdm)->sfMigration = dms[i]->sfMigration;
4367       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4368       (*superdm)->useNatural = PETSC_TRUE;
4369       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4370       (*superdm)->sfNatural = sfNatural;
4371       break;
4372     }
4373   }
4374   PetscFunctionReturn(PETSC_SUCCESS);
4375 }
4376 
4377 /*@
4378   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4379 
4380   Not Collective
4381 
4382   Input Parameter:
4383 . dm - The `DMPLEX`
4384 
4385   Level: beginner
4386 
4387   Note:
4388   This should be called after all calls to `DMPlexSetCone()`
4389 
4390 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4391 @*/
4392 PetscErrorCode DMPlexSymmetrize(DM dm)
4393 {
4394   DM_Plex  *mesh = (DM_Plex *)dm->data;
4395   PetscInt *offsets;
4396   PetscInt  supportSize;
4397   PetscInt  pStart, pEnd, p;
4398 
4399   PetscFunctionBegin;
4400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4401   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4402   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4403   /* Calculate support sizes */
4404   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4405   for (p = pStart; p < pEnd; ++p) {
4406     PetscInt dof, off, c;
4407 
4408     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4409     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4410     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4411   }
4412   PetscCall(PetscSectionSetUp(mesh->supportSection));
4413   /* Calculate supports */
4414   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4415   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4416   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4417   for (p = pStart; p < pEnd; ++p) {
4418     PetscInt dof, off, c;
4419 
4420     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4421     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4422     for (c = off; c < off + dof; ++c) {
4423       const PetscInt q = mesh->cones[c];
4424       PetscInt       offS;
4425 
4426       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4427 
4428       mesh->supports[offS + offsets[q]] = p;
4429       ++offsets[q];
4430     }
4431   }
4432   PetscCall(PetscFree(offsets));
4433   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4434   PetscFunctionReturn(PETSC_SUCCESS);
4435 }
4436 
4437 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4438 {
4439   IS stratumIS;
4440 
4441   PetscFunctionBegin;
4442   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4443   if (PetscDefined(USE_DEBUG)) {
4444     PetscInt  qStart, qEnd, numLevels, level;
4445     PetscBool overlap = PETSC_FALSE;
4446     PetscCall(DMLabelGetNumValues(label, &numLevels));
4447     for (level = 0; level < numLevels; level++) {
4448       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4449       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4450         overlap = PETSC_TRUE;
4451         break;
4452       }
4453     }
4454     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);
4455   }
4456   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4457   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4458   PetscCall(ISDestroy(&stratumIS));
4459   PetscFunctionReturn(PETSC_SUCCESS);
4460 }
4461 
4462 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4463 {
4464   PetscInt *pMin, *pMax;
4465   PetscInt  pStart, pEnd;
4466   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4467 
4468   PetscFunctionBegin;
4469   {
4470     DMLabel label2;
4471 
4472     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4473     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4474   }
4475   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4476   for (PetscInt p = pStart; p < pEnd; ++p) {
4477     DMPolytopeType ct;
4478 
4479     PetscCall(DMPlexGetCellType(dm, p, &ct));
4480     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4481     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4482   }
4483   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4484   for (PetscInt d = dmin; d <= dmax; ++d) {
4485     pMin[d] = PETSC_INT_MAX;
4486     pMax[d] = PETSC_INT_MIN;
4487   }
4488   for (PetscInt p = pStart; p < pEnd; ++p) {
4489     DMPolytopeType ct;
4490     PetscInt       d;
4491 
4492     PetscCall(DMPlexGetCellType(dm, p, &ct));
4493     d       = DMPolytopeTypeGetDim(ct);
4494     pMin[d] = PetscMin(p, pMin[d]);
4495     pMax[d] = PetscMax(p, pMax[d]);
4496   }
4497   for (PetscInt d = dmin; d <= dmax; ++d) {
4498     if (pMin[d] > pMax[d]) continue;
4499     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4500   }
4501   PetscCall(PetscFree2(pMin, pMax));
4502   PetscFunctionReturn(PETSC_SUCCESS);
4503 }
4504 
4505 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4506 {
4507   PetscInt pStart, pEnd;
4508   PetscInt numRoots = 0, numLeaves = 0;
4509 
4510   PetscFunctionBegin;
4511   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4512   {
4513     /* Initialize roots and count leaves */
4514     PetscInt sMin = PETSC_INT_MAX;
4515     PetscInt sMax = PETSC_INT_MIN;
4516     PetscInt coneSize, supportSize;
4517 
4518     for (PetscInt p = pStart; p < pEnd; ++p) {
4519       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4520       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4521       if (!coneSize && supportSize) {
4522         sMin = PetscMin(p, sMin);
4523         sMax = PetscMax(p, sMax);
4524         ++numRoots;
4525       } else if (!supportSize && coneSize) {
4526         ++numLeaves;
4527       } else if (!supportSize && !coneSize) {
4528         /* Isolated points */
4529         sMin = PetscMin(p, sMin);
4530         sMax = PetscMax(p, sMax);
4531       }
4532     }
4533     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4534   }
4535 
4536   if (numRoots + numLeaves == (pEnd - pStart)) {
4537     PetscInt sMin = PETSC_INT_MAX;
4538     PetscInt sMax = PETSC_INT_MIN;
4539     PetscInt coneSize, supportSize;
4540 
4541     for (PetscInt p = pStart; p < pEnd; ++p) {
4542       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4543       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4544       if (!supportSize && coneSize) {
4545         sMin = PetscMin(p, sMin);
4546         sMax = PetscMax(p, sMax);
4547       }
4548     }
4549     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4550   } else {
4551     PetscInt level = 0;
4552     PetscInt qStart, qEnd;
4553 
4554     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4555     while (qEnd > qStart) {
4556       PetscInt sMin = PETSC_INT_MAX;
4557       PetscInt sMax = PETSC_INT_MIN;
4558 
4559       for (PetscInt q = qStart; q < qEnd; ++q) {
4560         const PetscInt *support;
4561         PetscInt        supportSize;
4562 
4563         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4564         PetscCall(DMPlexGetSupport(dm, q, &support));
4565         for (PetscInt s = 0; s < supportSize; ++s) {
4566           sMin = PetscMin(support[s], sMin);
4567           sMax = PetscMax(support[s], sMax);
4568         }
4569       }
4570       PetscCall(DMLabelGetNumValues(label, &level));
4571       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4572       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4573     }
4574   }
4575   PetscFunctionReturn(PETSC_SUCCESS);
4576 }
4577 
4578 /*@
4579   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4580 
4581   Collective
4582 
4583   Input Parameter:
4584 . dm - The `DMPLEX`
4585 
4586   Level: beginner
4587 
4588   Notes:
4589   The strata group all points of the same grade, and this function calculates the strata. This
4590   grade can be seen as the height (or depth) of the point in the DAG.
4591 
4592   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4593   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4594   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4595   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4596   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4597   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4598   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4599 
4600   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4601   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4602   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
4603   to interpolate only that one (e0), so that
4604 .vb
4605   cone(c0) = {e0, v2}
4606   cone(e0) = {v0, v1}
4607 .ve
4608   If `DMPlexStratify()` is run on this mesh, it will give depths
4609 .vb
4610    depth 0 = {v0, v1, v2}
4611    depth 1 = {e0, c0}
4612 .ve
4613   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4614 
4615   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4616 
4617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4618 @*/
4619 PetscErrorCode DMPlexStratify(DM dm)
4620 {
4621   DM_Plex  *mesh = (DM_Plex *)dm->data;
4622   DMLabel   label;
4623   PetscBool flg = PETSC_FALSE;
4624 
4625   PetscFunctionBegin;
4626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4627   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4628 
4629   // Create depth label
4630   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4631   PetscCall(DMCreateLabel(dm, "depth"));
4632   PetscCall(DMPlexGetDepthLabel(dm, &label));
4633 
4634   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4635   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4636   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4637 
4638   { /* just in case there is an empty process */
4639     PetscInt numValues, maxValues = 0, v;
4640 
4641     PetscCall(DMLabelGetNumValues(label, &numValues));
4642     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4643     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4644   }
4645   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4646   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4647   PetscFunctionReturn(PETSC_SUCCESS);
4648 }
4649 
4650 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4651 {
4652   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4653   PetscInt       dim, depth, pheight, coneSize;
4654   PetscBool      preferTensor;
4655 
4656   PetscFunctionBeginHot;
4657   PetscCall(DMGetDimension(dm, &dim));
4658   PetscCall(DMPlexGetDepth(dm, &depth));
4659   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4660   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4661   pheight = depth - pdepth;
4662   if (depth <= 1) {
4663     switch (pdepth) {
4664     case 0:
4665       ct = DM_POLYTOPE_POINT;
4666       break;
4667     case 1:
4668       switch (coneSize) {
4669       case 2:
4670         ct = DM_POLYTOPE_SEGMENT;
4671         break;
4672       case 3:
4673         ct = DM_POLYTOPE_TRIANGLE;
4674         break;
4675       case 4:
4676         switch (dim) {
4677         case 2:
4678           ct = DM_POLYTOPE_QUADRILATERAL;
4679           break;
4680         case 3:
4681           ct = DM_POLYTOPE_TETRAHEDRON;
4682           break;
4683         default:
4684           break;
4685         }
4686         break;
4687       case 5:
4688         ct = DM_POLYTOPE_PYRAMID;
4689         break;
4690       case 6:
4691         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4692         break;
4693       case 8:
4694         ct = DM_POLYTOPE_HEXAHEDRON;
4695         break;
4696       default:
4697         break;
4698       }
4699     }
4700   } else {
4701     if (pdepth == 0) {
4702       ct = DM_POLYTOPE_POINT;
4703     } else if (pheight == 0) {
4704       switch (dim) {
4705       case 1:
4706         switch (coneSize) {
4707         case 2:
4708           ct = DM_POLYTOPE_SEGMENT;
4709           break;
4710         default:
4711           break;
4712         }
4713         break;
4714       case 2:
4715         switch (coneSize) {
4716         case 3:
4717           ct = DM_POLYTOPE_TRIANGLE;
4718           break;
4719         case 4:
4720           ct = DM_POLYTOPE_QUADRILATERAL;
4721           break;
4722         default:
4723           break;
4724         }
4725         break;
4726       case 3:
4727         switch (coneSize) {
4728         case 4:
4729           ct = DM_POLYTOPE_TETRAHEDRON;
4730           break;
4731         case 5: {
4732           const PetscInt *cone;
4733           PetscInt        faceConeSize;
4734 
4735           PetscCall(DMPlexGetCone(dm, p, &cone));
4736           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4737           switch (faceConeSize) {
4738           case 3:
4739             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4740             break;
4741           case 4:
4742             ct = DM_POLYTOPE_PYRAMID;
4743             break;
4744           }
4745         } break;
4746         case 6:
4747           ct = DM_POLYTOPE_HEXAHEDRON;
4748           break;
4749         default:
4750           break;
4751         }
4752         break;
4753       default:
4754         break;
4755       }
4756     } else if (pheight > 0) {
4757       switch (coneSize) {
4758       case 2:
4759         ct = DM_POLYTOPE_SEGMENT;
4760         break;
4761       case 3:
4762         ct = DM_POLYTOPE_TRIANGLE;
4763         break;
4764       case 4:
4765         ct = DM_POLYTOPE_QUADRILATERAL;
4766         break;
4767       default:
4768         break;
4769       }
4770     }
4771   }
4772   *pt = ct;
4773   PetscFunctionReturn(PETSC_SUCCESS);
4774 }
4775 
4776 /*@
4777   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4778 
4779   Collective
4780 
4781   Input Parameter:
4782 . dm - The `DMPLEX`
4783 
4784   Level: developer
4785 
4786   Note:
4787   This function is normally called automatically when a cell type is requested. It creates an
4788   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4789   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4790 
4791   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4792 
4793 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4794 @*/
4795 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4796 {
4797   DM_Plex *mesh;
4798   DMLabel  ctLabel;
4799   PetscInt pStart, pEnd, p;
4800 
4801   PetscFunctionBegin;
4802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4803   mesh = (DM_Plex *)dm->data;
4804   PetscCall(DMCreateLabel(dm, "celltype"));
4805   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4806   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4807   PetscCall(PetscFree(mesh->cellTypes));
4808   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4809   for (p = pStart; p < pEnd; ++p) {
4810     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4811     PetscInt       pdepth;
4812 
4813     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4814     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4815     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]);
4816     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4817     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4818   }
4819   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4820   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4821   PetscFunctionReturn(PETSC_SUCCESS);
4822 }
4823 
4824 /*@C
4825   DMPlexGetJoin - Get an array for the join of the set of points
4826 
4827   Not Collective
4828 
4829   Input Parameters:
4830 + dm        - The `DMPLEX` object
4831 . numPoints - The number of input points for the join
4832 - points    - The input points
4833 
4834   Output Parameters:
4835 + numCoveredPoints - The number of points in the join
4836 - coveredPoints    - The points in the join
4837 
4838   Level: intermediate
4839 
4840   Note:
4841   Currently, this is restricted to a single level join
4842 
4843   Fortran Notes:
4844   `converedPoints` must be declared with
4845 .vb
4846   PetscInt, pointer :: coveredPints(:)
4847 .ve
4848 
4849 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4850 @*/
4851 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4852 {
4853   DM_Plex  *mesh = (DM_Plex *)dm->data;
4854   PetscInt *join[2];
4855   PetscInt  joinSize, i = 0;
4856   PetscInt  dof, off, p, c, m;
4857   PetscInt  maxSupportSize;
4858 
4859   PetscFunctionBegin;
4860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4861   PetscAssertPointer(points, 3);
4862   PetscAssertPointer(numCoveredPoints, 4);
4863   PetscAssertPointer(coveredPoints, 5);
4864   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4865   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4866   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4867   /* Copy in support of first point */
4868   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4869   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4870   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4871   /* Check each successive support */
4872   for (p = 1; p < numPoints; ++p) {
4873     PetscInt newJoinSize = 0;
4874 
4875     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4876     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4877     for (c = 0; c < dof; ++c) {
4878       const PetscInt point = mesh->supports[off + c];
4879 
4880       for (m = 0; m < joinSize; ++m) {
4881         if (point == join[i][m]) {
4882           join[1 - i][newJoinSize++] = point;
4883           break;
4884         }
4885       }
4886     }
4887     joinSize = newJoinSize;
4888     i        = 1 - i;
4889   }
4890   *numCoveredPoints = joinSize;
4891   *coveredPoints    = join[i];
4892   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4893   PetscFunctionReturn(PETSC_SUCCESS);
4894 }
4895 
4896 /*@C
4897   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4898 
4899   Not Collective
4900 
4901   Input Parameters:
4902 + dm        - The `DMPLEX` object
4903 . numPoints - The number of input points for the join
4904 - points    - The input points
4905 
4906   Output Parameters:
4907 + numCoveredPoints - The number of points in the join
4908 - coveredPoints    - The points in the join
4909 
4910   Level: intermediate
4911 
4912   Fortran Notes:
4913   `converedPoints` must be declared with
4914 .vb
4915   PetscInt, pointer :: coveredPoints(:)
4916 .ve
4917 
4918   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4919 
4920 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4921 @*/
4922 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4923 {
4924   PetscFunctionBegin;
4925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4926   if (points) PetscAssertPointer(points, 3);
4927   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4928   PetscAssertPointer(coveredPoints, 5);
4929   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4930   if (numCoveredPoints) *numCoveredPoints = 0;
4931   PetscFunctionReturn(PETSC_SUCCESS);
4932 }
4933 
4934 /*@C
4935   DMPlexGetFullJoin - Get an array for the join of the set of points
4936 
4937   Not Collective
4938 
4939   Input Parameters:
4940 + dm        - The `DMPLEX` object
4941 . numPoints - The number of input points for the join
4942 - points    - The input points, its length is `numPoints`
4943 
4944   Output Parameters:
4945 + numCoveredPoints - The number of points in the join
4946 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4947 
4948   Level: intermediate
4949 
4950   Fortran Notes:
4951 .vb
4952   PetscInt, pointer :: coveredPints(:)
4953 .ve
4954 
4955 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4956 @*/
4957 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4958 {
4959   PetscInt *offsets, **closures;
4960   PetscInt *join[2];
4961   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4962   PetscInt  p, d, c, m, ms;
4963 
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   PetscAssertPointer(points, 3);
4967   PetscAssertPointer(numCoveredPoints, 4);
4968   PetscAssertPointer(coveredPoints, 5);
4969 
4970   PetscCall(DMPlexGetDepth(dm, &depth));
4971   PetscCall(PetscCalloc1(numPoints, &closures));
4972   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4973   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4974   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4975   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4976   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4977 
4978   for (p = 0; p < numPoints; ++p) {
4979     PetscInt closureSize;
4980 
4981     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4982 
4983     offsets[p * (depth + 2) + 0] = 0;
4984     for (d = 0; d < depth + 1; ++d) {
4985       PetscInt pStart, pEnd, i;
4986 
4987       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4988       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4989         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4990           offsets[p * (depth + 2) + d + 1] = i;
4991           break;
4992         }
4993       }
4994       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4995     }
4996     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);
4997   }
4998   for (d = 0; d < depth + 1; ++d) {
4999     PetscInt dof;
5000 
5001     /* Copy in support of first point */
5002     dof = offsets[d + 1] - offsets[d];
5003     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
5004     /* Check each successive cone */
5005     for (p = 1; p < numPoints && joinSize; ++p) {
5006       PetscInt newJoinSize = 0;
5007 
5008       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
5009       for (c = 0; c < dof; ++c) {
5010         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
5011 
5012         for (m = 0; m < joinSize; ++m) {
5013           if (point == join[i][m]) {
5014             join[1 - i][newJoinSize++] = point;
5015             break;
5016           }
5017         }
5018       }
5019       joinSize = newJoinSize;
5020       i        = 1 - i;
5021     }
5022     if (joinSize) break;
5023   }
5024   *numCoveredPoints = joinSize;
5025   *coveredPoints    = join[i];
5026   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5027   PetscCall(PetscFree(closures));
5028   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5029   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5030   PetscFunctionReturn(PETSC_SUCCESS);
5031 }
5032 
5033 /*@C
5034   DMPlexGetMeet - Get an array for the meet of the set of points
5035 
5036   Not Collective
5037 
5038   Input Parameters:
5039 + dm        - The `DMPLEX` object
5040 . numPoints - The number of input points for the meet
5041 - points    - The input points, of length `numPoints`
5042 
5043   Output Parameters:
5044 + numCoveringPoints - The number of points in the meet
5045 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5046 
5047   Level: intermediate
5048 
5049   Note:
5050   Currently, this is restricted to a single level meet
5051 
5052   Fortran Note:
5053   `coveringPoints` must be declared with
5054 .vb
5055   PetscInt, pointer :: coveringPoints(:)
5056 .ve
5057 
5058 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5059 @*/
5060 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5061 {
5062   DM_Plex  *mesh = (DM_Plex *)dm->data;
5063   PetscInt *meet[2];
5064   PetscInt  meetSize, i = 0;
5065   PetscInt  dof, off, p, c, m;
5066   PetscInt  maxConeSize;
5067 
5068   PetscFunctionBegin;
5069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5070   PetscAssertPointer(points, 3);
5071   PetscAssertPointer(numCoveringPoints, 4);
5072   PetscAssertPointer(coveringPoints, 5);
5073   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5074   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5075   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5076   /* Copy in cone of first point */
5077   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5078   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5079   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5080   /* Check each successive cone */
5081   for (p = 1; p < numPoints; ++p) {
5082     PetscInt newMeetSize = 0;
5083 
5084     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5085     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5086     for (c = 0; c < dof; ++c) {
5087       const PetscInt point = mesh->cones[off + c];
5088 
5089       for (m = 0; m < meetSize; ++m) {
5090         if (point == meet[i][m]) {
5091           meet[1 - i][newMeetSize++] = point;
5092           break;
5093         }
5094       }
5095     }
5096     meetSize = newMeetSize;
5097     i        = 1 - i;
5098   }
5099   *numCoveringPoints = meetSize;
5100   *coveringPoints    = meet[i];
5101   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5102   PetscFunctionReturn(PETSC_SUCCESS);
5103 }
5104 
5105 /*@C
5106   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5107 
5108   Not Collective
5109 
5110   Input Parameters:
5111 + dm        - The `DMPLEX` object
5112 . numPoints - The number of input points for the meet
5113 - points    - The input points
5114 
5115   Output Parameters:
5116 + numCoveredPoints - The number of points in the meet
5117 - coveredPoints    - The points in the meet
5118 
5119   Level: intermediate
5120 
5121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5122 @*/
5123 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5124 {
5125   PetscFunctionBegin;
5126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5127   if (points) PetscAssertPointer(points, 3);
5128   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5129   PetscAssertPointer(coveredPoints, 5);
5130   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5131   if (numCoveredPoints) *numCoveredPoints = 0;
5132   PetscFunctionReturn(PETSC_SUCCESS);
5133 }
5134 
5135 /*@C
5136   DMPlexGetFullMeet - Get an array for the meet of the set of points
5137 
5138   Not Collective
5139 
5140   Input Parameters:
5141 + dm        - The `DMPLEX` object
5142 . numPoints - The number of input points for the meet
5143 - points    - The input points, of length  `numPoints`
5144 
5145   Output Parameters:
5146 + numCoveredPoints - The number of points in the meet
5147 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5148 
5149   Level: intermediate
5150 
5151   Fortran Notes:
5152   `coveredPoints` must be declared with
5153 .vb
5154   PetscInt, pointer :: coveredPoints(:)
5155 .ve
5156 
5157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5158 @*/
5159 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5160 {
5161   PetscInt *offsets, **closures;
5162   PetscInt *meet[2];
5163   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5164   PetscInt  p, h, c, m, mc;
5165 
5166   PetscFunctionBegin;
5167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5168   PetscAssertPointer(points, 3);
5169   PetscAssertPointer(numCoveredPoints, 4);
5170   PetscAssertPointer(coveredPoints, 5);
5171 
5172   PetscCall(DMPlexGetDepth(dm, &height));
5173   PetscCall(PetscMalloc1(numPoints, &closures));
5174   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5175   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5176   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5177   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5178   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5179 
5180   for (p = 0; p < numPoints; ++p) {
5181     PetscInt closureSize;
5182 
5183     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5184 
5185     offsets[p * (height + 2) + 0] = 0;
5186     for (h = 0; h < height + 1; ++h) {
5187       PetscInt pStart, pEnd, i;
5188 
5189       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5190       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5191         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5192           offsets[p * (height + 2) + h + 1] = i;
5193           break;
5194         }
5195       }
5196       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5197     }
5198     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);
5199   }
5200   for (h = 0; h < height + 1; ++h) {
5201     PetscInt dof;
5202 
5203     /* Copy in cone of first point */
5204     dof = offsets[h + 1] - offsets[h];
5205     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5206     /* Check each successive cone */
5207     for (p = 1; p < numPoints && meetSize; ++p) {
5208       PetscInt newMeetSize = 0;
5209 
5210       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5211       for (c = 0; c < dof; ++c) {
5212         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5213 
5214         for (m = 0; m < meetSize; ++m) {
5215           if (point == meet[i][m]) {
5216             meet[1 - i][newMeetSize++] = point;
5217             break;
5218           }
5219         }
5220       }
5221       meetSize = newMeetSize;
5222       i        = 1 - i;
5223     }
5224     if (meetSize) break;
5225   }
5226   *numCoveredPoints = meetSize;
5227   *coveredPoints    = meet[i];
5228   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5229   PetscCall(PetscFree(closures));
5230   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5231   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5232   PetscFunctionReturn(PETSC_SUCCESS);
5233 }
5234 
5235 /*@
5236   DMPlexEqual - Determine if two `DM` have the same topology
5237 
5238   Not Collective
5239 
5240   Input Parameters:
5241 + dmA - A `DMPLEX` object
5242 - dmB - A `DMPLEX` object
5243 
5244   Output Parameter:
5245 . equal - `PETSC_TRUE` if the topologies are identical
5246 
5247   Level: intermediate
5248 
5249   Note:
5250   We are not solving graph isomorphism, so we do not permute.
5251 
5252 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5253 @*/
5254 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5255 {
5256   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5257 
5258   PetscFunctionBegin;
5259   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5260   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5261   PetscAssertPointer(equal, 3);
5262 
5263   *equal = PETSC_FALSE;
5264   PetscCall(DMPlexGetDepth(dmA, &depth));
5265   PetscCall(DMPlexGetDepth(dmB, &depthB));
5266   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5267   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5268   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5269   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5270   for (p = pStart; p < pEnd; ++p) {
5271     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5272     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5273 
5274     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5275     PetscCall(DMPlexGetCone(dmA, p, &cone));
5276     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5277     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5278     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5279     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5280     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5281     for (c = 0; c < coneSize; ++c) {
5282       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5283       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5284     }
5285     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5286     PetscCall(DMPlexGetSupport(dmA, p, &support));
5287     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5288     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5289     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5290     for (s = 0; s < supportSize; ++s) {
5291       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5292     }
5293   }
5294   *equal = PETSC_TRUE;
5295   PetscFunctionReturn(PETSC_SUCCESS);
5296 }
5297 
5298 /*@
5299   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5300 
5301   Not Collective
5302 
5303   Input Parameters:
5304 + dm         - The `DMPLEX`
5305 . cellDim    - The cell dimension
5306 - numCorners - The number of vertices on a cell
5307 
5308   Output Parameter:
5309 . numFaceVertices - The number of vertices on a face
5310 
5311   Level: developer
5312 
5313   Note:
5314   Of course this can only work for a restricted set of symmetric shapes
5315 
5316 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5317 @*/
5318 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5319 {
5320   MPI_Comm comm;
5321 
5322   PetscFunctionBegin;
5323   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5324   PetscAssertPointer(numFaceVertices, 4);
5325   switch (cellDim) {
5326   case 0:
5327     *numFaceVertices = 0;
5328     break;
5329   case 1:
5330     *numFaceVertices = 1;
5331     break;
5332   case 2:
5333     switch (numCorners) {
5334     case 3:                 /* triangle */
5335       *numFaceVertices = 2; /* Edge has 2 vertices */
5336       break;
5337     case 4:                 /* quadrilateral */
5338       *numFaceVertices = 2; /* Edge has 2 vertices */
5339       break;
5340     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5341       *numFaceVertices = 3; /* Edge has 3 vertices */
5342       break;
5343     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5344       *numFaceVertices = 3; /* Edge has 3 vertices */
5345       break;
5346     default:
5347       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5348     }
5349     break;
5350   case 3:
5351     switch (numCorners) {
5352     case 4:                 /* tetradehdron */
5353       *numFaceVertices = 3; /* Face has 3 vertices */
5354       break;
5355     case 6:                 /* tet cohesive cells */
5356       *numFaceVertices = 4; /* Face has 4 vertices */
5357       break;
5358     case 8:                 /* hexahedron */
5359       *numFaceVertices = 4; /* Face has 4 vertices */
5360       break;
5361     case 9:                 /* tet cohesive Lagrange cells */
5362       *numFaceVertices = 6; /* Face has 6 vertices */
5363       break;
5364     case 10:                /* quadratic tetrahedron */
5365       *numFaceVertices = 6; /* Face has 6 vertices */
5366       break;
5367     case 12:                /* hex cohesive Lagrange cells */
5368       *numFaceVertices = 6; /* Face has 6 vertices */
5369       break;
5370     case 18:                /* quadratic tet cohesive Lagrange cells */
5371       *numFaceVertices = 6; /* Face has 6 vertices */
5372       break;
5373     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5374       *numFaceVertices = 9; /* Face has 9 vertices */
5375       break;
5376     default:
5377       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5378     }
5379     break;
5380   default:
5381     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5382   }
5383   PetscFunctionReturn(PETSC_SUCCESS);
5384 }
5385 
5386 /*@
5387   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5388 
5389   Not Collective
5390 
5391   Input Parameter:
5392 . dm - The `DMPLEX` object
5393 
5394   Output Parameter:
5395 . depthLabel - The `DMLabel` recording point depth
5396 
5397   Level: developer
5398 
5399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5400 @*/
5401 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5402 {
5403   PetscFunctionBegin;
5404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5405   PetscAssertPointer(depthLabel, 2);
5406   *depthLabel = dm->depthLabel;
5407   PetscFunctionReturn(PETSC_SUCCESS);
5408 }
5409 
5410 /*@
5411   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5412 
5413   Not Collective
5414 
5415   Input Parameter:
5416 . dm - The `DMPLEX` object
5417 
5418   Output Parameter:
5419 . depth - The number of strata (breadth first levels) in the DAG
5420 
5421   Level: developer
5422 
5423   Notes:
5424   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5425 
5426   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5427 
5428   An empty mesh gives -1.
5429 
5430 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5431 @*/
5432 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5433 {
5434   DM_Plex *mesh = (DM_Plex *)dm->data;
5435   DMLabel  label;
5436   PetscInt d = -1;
5437 
5438   PetscFunctionBegin;
5439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5440   PetscAssertPointer(depth, 2);
5441   if (mesh->tr) {
5442     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5443   } else {
5444     PetscCall(DMPlexGetDepthLabel(dm, &label));
5445     // Allow missing depths
5446     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5447     *depth = d;
5448   }
5449   PetscFunctionReturn(PETSC_SUCCESS);
5450 }
5451 
5452 /*@
5453   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5454 
5455   Not Collective
5456 
5457   Input Parameters:
5458 + dm    - The `DMPLEX` object
5459 - depth - The requested depth
5460 
5461   Output Parameters:
5462 + start - The first point at this `depth`
5463 - end   - One beyond the last point at this `depth`
5464 
5465   Level: developer
5466 
5467   Notes:
5468   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5469   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5470   higher dimension, e.g., "edges".
5471 
5472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5473 @*/
5474 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5475 {
5476   DM_Plex *mesh = (DM_Plex *)dm->data;
5477   DMLabel  label;
5478   PetscInt pStart, pEnd;
5479 
5480   PetscFunctionBegin;
5481   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5482   if (start) {
5483     PetscAssertPointer(start, 3);
5484     *start = 0;
5485   }
5486   if (end) {
5487     PetscAssertPointer(end, 4);
5488     *end = 0;
5489   }
5490   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5491   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5492   if (depth < 0) {
5493     if (start) *start = pStart;
5494     if (end) *end = pEnd;
5495     PetscFunctionReturn(PETSC_SUCCESS);
5496   }
5497   if (mesh->tr) {
5498     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5499   } else {
5500     PetscCall(DMPlexGetDepthLabel(dm, &label));
5501     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5502     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5503   }
5504   PetscFunctionReturn(PETSC_SUCCESS);
5505 }
5506 
5507 /*@
5508   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5509 
5510   Not Collective
5511 
5512   Input Parameters:
5513 + dm     - The `DMPLEX` object
5514 - height - The requested height
5515 
5516   Output Parameters:
5517 + start - The first point at this `height`
5518 - end   - One beyond the last point at this `height`
5519 
5520   Level: developer
5521 
5522   Notes:
5523   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5524   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5525   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5526 
5527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5528 @*/
5529 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5530 {
5531   DMLabel  label;
5532   PetscInt depth, pStart, pEnd;
5533 
5534   PetscFunctionBegin;
5535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5536   if (start) {
5537     PetscAssertPointer(start, 3);
5538     *start = 0;
5539   }
5540   if (end) {
5541     PetscAssertPointer(end, 4);
5542     *end = 0;
5543   }
5544   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5545   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5546   if (height < 0) {
5547     if (start) *start = pStart;
5548     if (end) *end = pEnd;
5549     PetscFunctionReturn(PETSC_SUCCESS);
5550   }
5551   PetscCall(DMPlexGetDepthLabel(dm, &label));
5552   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5553   else PetscCall(DMGetDimension(dm, &depth));
5554   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5555   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5556   PetscFunctionReturn(PETSC_SUCCESS);
5557 }
5558 
5559 /*@
5560   DMPlexGetPointDepth - Get the `depth` of a given point
5561 
5562   Not Collective
5563 
5564   Input Parameters:
5565 + dm    - The `DMPLEX` object
5566 - point - The point
5567 
5568   Output Parameter:
5569 . depth - The depth of the `point`
5570 
5571   Level: intermediate
5572 
5573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5574 @*/
5575 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5576 {
5577   PetscFunctionBegin;
5578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5579   PetscAssertPointer(depth, 3);
5580   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5581   PetscFunctionReturn(PETSC_SUCCESS);
5582 }
5583 
5584 /*@
5585   DMPlexGetPointHeight - Get the `height` of a given point
5586 
5587   Not Collective
5588 
5589   Input Parameters:
5590 + dm    - The `DMPLEX` object
5591 - point - The point
5592 
5593   Output Parameter:
5594 . height - The height of the `point`
5595 
5596   Level: intermediate
5597 
5598 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5599 @*/
5600 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5601 {
5602   PetscInt n, pDepth;
5603 
5604   PetscFunctionBegin;
5605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5606   PetscAssertPointer(height, 3);
5607   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5608   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5609   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5610   PetscFunctionReturn(PETSC_SUCCESS);
5611 }
5612 
5613 /*@
5614   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5615 
5616   Not Collective
5617 
5618   Input Parameter:
5619 . dm - The `DMPLEX` object
5620 
5621   Output Parameter:
5622 . celltypeLabel - The `DMLabel` recording cell polytope type
5623 
5624   Level: developer
5625 
5626   Note:
5627   This function will trigger automatica computation of cell types. This can be disabled by calling
5628   `DMCreateLabel`(dm, "celltype") beforehand.
5629 
5630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5631 @*/
5632 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5633 {
5634   PetscFunctionBegin;
5635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5636   PetscAssertPointer(celltypeLabel, 2);
5637   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5638   *celltypeLabel = dm->celltypeLabel;
5639   PetscFunctionReturn(PETSC_SUCCESS);
5640 }
5641 
5642 /*@
5643   DMPlexGetCellType - Get the polytope type of a given cell
5644 
5645   Not Collective
5646 
5647   Input Parameters:
5648 + dm   - The `DMPLEX` object
5649 - cell - The cell
5650 
5651   Output Parameter:
5652 . celltype - The polytope type of the cell
5653 
5654   Level: intermediate
5655 
5656 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5657 @*/
5658 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5659 {
5660   DM_Plex *mesh = (DM_Plex *)dm->data;
5661   DMLabel  label;
5662   PetscInt ct;
5663 
5664   PetscFunctionBegin;
5665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5666   PetscAssertPointer(celltype, 3);
5667   if (mesh->tr) {
5668     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5669   } else {
5670     PetscInt pStart, pEnd;
5671 
5672     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5673     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5674       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5675       if (pEnd <= pStart) {
5676         *celltype = DM_POLYTOPE_UNKNOWN;
5677         PetscFunctionReturn(PETSC_SUCCESS);
5678       }
5679       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5680       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5681       for (PetscInt p = pStart; p < pEnd; p++) {
5682         PetscCall(DMLabelGetValue(label, p, &ct));
5683         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5684       }
5685     }
5686     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5687     if (PetscDefined(USE_DEBUG)) {
5688       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5689       PetscCall(DMLabelGetValue(label, cell, &ct));
5690       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5691       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5692     }
5693   }
5694   PetscFunctionReturn(PETSC_SUCCESS);
5695 }
5696 
5697 /*@
5698   DMPlexSetCellType - Set the polytope type of a given cell
5699 
5700   Not Collective
5701 
5702   Input Parameters:
5703 + dm       - The `DMPLEX` object
5704 . cell     - The cell
5705 - celltype - The polytope type of the cell
5706 
5707   Level: advanced
5708 
5709   Note:
5710   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5711   is executed. This function will override the computed type. However, if automatic classification will not succeed
5712   and a user wants to manually specify all types, the classification must be disabled by calling
5713   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5714 
5715 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5716 @*/
5717 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5718 {
5719   DM_Plex *mesh = (DM_Plex *)dm->data;
5720   DMLabel  label;
5721   PetscInt pStart, pEnd;
5722 
5723   PetscFunctionBegin;
5724   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5725   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5726   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5727   PetscCall(DMLabelSetValue(label, cell, celltype));
5728   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5729   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5730   PetscFunctionReturn(PETSC_SUCCESS);
5731 }
5732 
5733 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5734 {
5735   PetscSection section;
5736   PetscInt     maxHeight;
5737   const char  *prefix;
5738 
5739   PetscFunctionBegin;
5740   PetscCall(DMClone(dm, cdm));
5741   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5742   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5743   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5744   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5745   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5746   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5747   PetscCall(DMSetLocalSection(*cdm, section));
5748   PetscCall(PetscSectionDestroy(&section));
5749 
5750   PetscCall(DMSetNumFields(*cdm, 1));
5751   PetscCall(DMCreateDS(*cdm));
5752   (*cdm)->cloneOpts = PETSC_TRUE;
5753   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5754   PetscFunctionReturn(PETSC_SUCCESS);
5755 }
5756 
5757 PetscErrorCode DMCreateCellCoordinateDM_Plex(DM dm, DM *cdm)
5758 {
5759   DM           cgcdm;
5760   PetscSection section;
5761   const char  *prefix;
5762 
5763   PetscFunctionBegin;
5764   PetscCall(DMGetCoordinateDM(dm, &cgcdm));
5765   PetscCall(DMClone(cgcdm, cdm));
5766   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5767   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5768   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cellcdm_"));
5769   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5770   PetscCall(DMSetLocalSection(*cdm, section));
5771   PetscCall(PetscSectionDestroy(&section));
5772   PetscCall(DMSetNumFields(*cdm, 1));
5773   PetscCall(DMCreateDS(*cdm));
5774   (*cdm)->cloneOpts = PETSC_TRUE;
5775   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5776   PetscFunctionReturn(PETSC_SUCCESS);
5777 }
5778 
5779 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5780 {
5781   Vec coordsLocal, cellCoordsLocal;
5782   DM  coordsDM, cellCoordsDM;
5783 
5784   PetscFunctionBegin;
5785   *field = NULL;
5786   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5787   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5788   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5789   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5790   if (coordsLocal && coordsDM) {
5791     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5792     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5793   }
5794   PetscFunctionReturn(PETSC_SUCCESS);
5795 }
5796 
5797 /*@
5798   DMPlexGetConeSection - Return a section which describes the layout of cone data
5799 
5800   Not Collective
5801 
5802   Input Parameter:
5803 . dm - The `DMPLEX` object
5804 
5805   Output Parameter:
5806 . section - The `PetscSection` object
5807 
5808   Level: developer
5809 
5810 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5811 @*/
5812 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5813 {
5814   DM_Plex *mesh = (DM_Plex *)dm->data;
5815 
5816   PetscFunctionBegin;
5817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5818   if (section) *section = mesh->coneSection;
5819   PetscFunctionReturn(PETSC_SUCCESS);
5820 }
5821 
5822 /*@
5823   DMPlexGetSupportSection - Return a section which describes the layout of support data
5824 
5825   Not Collective
5826 
5827   Input Parameter:
5828 . dm - The `DMPLEX` object
5829 
5830   Output Parameter:
5831 . section - The `PetscSection` object
5832 
5833   Level: developer
5834 
5835 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5836 @*/
5837 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5838 {
5839   DM_Plex *mesh = (DM_Plex *)dm->data;
5840 
5841   PetscFunctionBegin;
5842   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5843   if (section) *section = mesh->supportSection;
5844   PetscFunctionReturn(PETSC_SUCCESS);
5845 }
5846 
5847 /*@C
5848   DMPlexGetCones - Return cone data
5849 
5850   Not Collective
5851 
5852   Input Parameter:
5853 . dm - The `DMPLEX` object
5854 
5855   Output Parameter:
5856 . cones - The cone for each point
5857 
5858   Level: developer
5859 
5860 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5861 @*/
5862 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5863 {
5864   DM_Plex *mesh = (DM_Plex *)dm->data;
5865 
5866   PetscFunctionBegin;
5867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5868   if (cones) *cones = mesh->cones;
5869   PetscFunctionReturn(PETSC_SUCCESS);
5870 }
5871 
5872 /*@C
5873   DMPlexGetConeOrientations - Return cone orientation data
5874 
5875   Not Collective
5876 
5877   Input Parameter:
5878 . dm - The `DMPLEX` object
5879 
5880   Output Parameter:
5881 . coneOrientations - The array of cone orientations for all points
5882 
5883   Level: developer
5884 
5885   Notes:
5886   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5887   as returned by `DMPlexGetConeOrientation()`.
5888 
5889   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5890 
5891 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5892 @*/
5893 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5894 {
5895   DM_Plex *mesh = (DM_Plex *)dm->data;
5896 
5897   PetscFunctionBegin;
5898   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5899   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5900   PetscFunctionReturn(PETSC_SUCCESS);
5901 }
5902 
5903 /* FEM Support */
5904 
5905 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5906 {
5907   PetscInt depth;
5908 
5909   PetscFunctionBegin;
5910   PetscCall(DMPlexGetDepth(plex, &depth));
5911   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5912   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5913   PetscFunctionReturn(PETSC_SUCCESS);
5914 }
5915 
5916 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5917 {
5918   PetscInt depth;
5919 
5920   PetscFunctionBegin;
5921   PetscCall(DMPlexGetDepth(plex, &depth));
5922   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5923   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5924   PetscFunctionReturn(PETSC_SUCCESS);
5925 }
5926 
5927 /*
5928  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5929  representing a line in the section.
5930 */
5931 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5932 {
5933   PetscObject  obj;
5934   PetscClassId id;
5935   PetscFE      fe = NULL;
5936 
5937   PetscFunctionBeginHot;
5938   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5939   PetscCall(DMGetField(dm, field, NULL, &obj));
5940   PetscCall(PetscObjectGetClassId(obj, &id));
5941   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5942 
5943   if (!fe) {
5944     /* Assume the full interpolated mesh is in the chart; lines in particular */
5945     /* An order k SEM disc has k-1 dofs on an edge */
5946     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5947     *k = *k / *Nc + 1;
5948   } else {
5949     PetscInt       dual_space_size, dim;
5950     PetscDualSpace dsp;
5951 
5952     PetscCall(DMGetDimension(dm, &dim));
5953     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5954     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5955     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5956     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5957     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5958   }
5959   PetscFunctionReturn(PETSC_SUCCESS);
5960 }
5961 
5962 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5963 {
5964   PetscFunctionBeginHot;
5965   if (tensor) {
5966     *dof = PetscPowInt(k + 1, dim);
5967   } else {
5968     switch (dim) {
5969     case 1:
5970       *dof = k + 1;
5971       break;
5972     case 2:
5973       *dof = ((k + 1) * (k + 2)) / 2;
5974       break;
5975     case 3:
5976       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5977       break;
5978     default:
5979       *dof = 0;
5980     }
5981   }
5982   PetscFunctionReturn(PETSC_SUCCESS);
5983 }
5984 
5985 /*@
5986   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5987   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5988   section provided (or the section of the `DM`).
5989 
5990   Input Parameters:
5991 + dm      - The `DM`
5992 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5993 - section - The `PetscSection` to reorder, or `NULL` for the default section
5994 
5995   Example:
5996   A typical interpolated single-quad mesh might order points as
5997 .vb
5998   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5999 
6000   v4 -- e6 -- v3
6001   |           |
6002   e7    c0    e8
6003   |           |
6004   v1 -- e5 -- v2
6005 .ve
6006 
6007   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
6008   dofs in the order of points, e.g.,
6009 .vb
6010     c0 -> [0,1,2,3]
6011     v1 -> [4]
6012     ...
6013     e5 -> [8, 9]
6014 .ve
6015 
6016   which corresponds to the dofs
6017 .vb
6018     6   10  11  7
6019     13  2   3   15
6020     12  0   1   14
6021     4   8   9   5
6022 .ve
6023 
6024   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
6025 .vb
6026   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
6027 .ve
6028 
6029   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
6030 .vb
6031    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6032 .ve
6033 
6034   Level: developer
6035 
6036   Notes:
6037   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6038   degree of the basis.
6039 
6040   This is required to run with libCEED.
6041 
6042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6043 @*/
6044 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6045 {
6046   DMLabel   label;
6047   PetscInt  dim, depth = -1, eStart = -1, Nf;
6048   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6049 
6050   PetscFunctionBegin;
6051   PetscCall(DMGetDimension(dm, &dim));
6052   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6053   if (point < 0) {
6054     PetscInt sStart, sEnd;
6055 
6056     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6057     point = sEnd - sStart ? sStart : point;
6058   }
6059   PetscCall(DMPlexGetDepthLabel(dm, &label));
6060   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6061   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6062   if (depth == 1) {
6063     eStart = point;
6064   } else if (depth == dim) {
6065     const PetscInt *cone;
6066 
6067     PetscCall(DMPlexGetCone(dm, point, &cone));
6068     if (dim == 2) eStart = cone[0];
6069     else if (dim == 3) {
6070       const PetscInt *cone2;
6071       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6072       eStart = cone2[0];
6073     } 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);
6074   } 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);
6075 
6076   PetscCall(PetscSectionGetNumFields(section, &Nf));
6077   for (PetscInt d = 1; d <= dim; d++) {
6078     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6079     PetscInt *perm;
6080 
6081     for (f = 0; f < Nf; ++f) {
6082       PetscInt dof;
6083 
6084       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6085       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6086       if (!continuous && d < dim) continue;
6087       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6088       size += dof * Nc;
6089     }
6090     PetscCall(PetscMalloc1(size, &perm));
6091     for (f = 0; f < Nf; ++f) {
6092       switch (d) {
6093       case 1:
6094         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6095         if (!continuous && d < dim) continue;
6096         /*
6097          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6098          We want              [ vtx0; edge of length k-1; vtx1 ]
6099          */
6100         if (continuous) {
6101           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6102           for (i = 0; i < k - 1; i++)
6103             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6104           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6105           foffset = offset;
6106         } else {
6107           PetscInt dof;
6108 
6109           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6110           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6111           foffset = offset;
6112         }
6113         break;
6114       case 2:
6115         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6116         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6117         if (!continuous && d < dim) continue;
6118         /* The SEM order is
6119 
6120          v_lb, {e_b}, v_rb,
6121          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6122          v_lt, reverse {e_t}, v_rt
6123          */
6124         if (continuous) {
6125           const PetscInt of   = 0;
6126           const PetscInt oeb  = of + PetscSqr(k - 1);
6127           const PetscInt oer  = oeb + (k - 1);
6128           const PetscInt oet  = oer + (k - 1);
6129           const PetscInt oel  = oet + (k - 1);
6130           const PetscInt ovlb = oel + (k - 1);
6131           const PetscInt ovrb = ovlb + 1;
6132           const PetscInt ovrt = ovrb + 1;
6133           const PetscInt ovlt = ovrt + 1;
6134           PetscInt       o;
6135 
6136           /* bottom */
6137           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6138           for (o = oeb; o < oer; ++o)
6139             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6140           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6141           /* middle */
6142           for (i = 0; i < k - 1; ++i) {
6143             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6144             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6145               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6146             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6147           }
6148           /* top */
6149           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6150           for (o = oel - 1; o >= oet; --o)
6151             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6152           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6153           foffset = offset;
6154         } else {
6155           PetscInt dof;
6156 
6157           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6158           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6159           foffset = offset;
6160         }
6161         break;
6162       case 3:
6163         /* The original hex closure is
6164 
6165          {c,
6166          f_b, f_t, f_f, f_b, f_r, f_l,
6167          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6168          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6169          */
6170         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6171         if (!continuous && d < dim) continue;
6172         /* The SEM order is
6173          Bottom Slice
6174          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6175          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6176          v_blb, {e_bb}, v_brb,
6177 
6178          Middle Slice (j)
6179          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6180          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6181          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6182 
6183          Top Slice
6184          v_tlf, {e_tf}, v_trf,
6185          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6186          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6187          */
6188         if (continuous) {
6189           const PetscInt oc    = 0;
6190           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6191           const PetscInt oft   = ofb + PetscSqr(k - 1);
6192           const PetscInt off   = oft + PetscSqr(k - 1);
6193           const PetscInt ofk   = off + PetscSqr(k - 1);
6194           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6195           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6196           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6197           const PetscInt oebb  = oebl + (k - 1);
6198           const PetscInt oebr  = oebb + (k - 1);
6199           const PetscInt oebf  = oebr + (k - 1);
6200           const PetscInt oetf  = oebf + (k - 1);
6201           const PetscInt oetr  = oetf + (k - 1);
6202           const PetscInt oetb  = oetr + (k - 1);
6203           const PetscInt oetl  = oetb + (k - 1);
6204           const PetscInt oerf  = oetl + (k - 1);
6205           const PetscInt oelf  = oerf + (k - 1);
6206           const PetscInt oelb  = oelf + (k - 1);
6207           const PetscInt oerb  = oelb + (k - 1);
6208           const PetscInt ovblf = oerb + (k - 1);
6209           const PetscInt ovblb = ovblf + 1;
6210           const PetscInt ovbrb = ovblb + 1;
6211           const PetscInt ovbrf = ovbrb + 1;
6212           const PetscInt ovtlf = ovbrf + 1;
6213           const PetscInt ovtrf = ovtlf + 1;
6214           const PetscInt ovtrb = ovtrf + 1;
6215           const PetscInt ovtlb = ovtrb + 1;
6216           PetscInt       o, n;
6217 
6218           /* Bottom Slice */
6219           /*   bottom */
6220           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6221           for (o = oetf - 1; o >= oebf; --o)
6222             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6223           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6224           /*   middle */
6225           for (i = 0; i < k - 1; ++i) {
6226             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6227             for (n = 0; n < k - 1; ++n) {
6228               o = ofb + n * (k - 1) + i;
6229               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6230             }
6231             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6232           }
6233           /*   top */
6234           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6235           for (o = oebb; o < oebr; ++o)
6236             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6237           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6238 
6239           /* Middle Slice */
6240           for (j = 0; j < k - 1; ++j) {
6241             /*   bottom */
6242             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6243             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6244               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6245             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6246             /*   middle */
6247             for (i = 0; i < k - 1; ++i) {
6248               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6249               for (n = 0; n < k - 1; ++n)
6250                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6251               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6252             }
6253             /*   top */
6254             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6255             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6256               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6257             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6258           }
6259 
6260           /* Top Slice */
6261           /*   bottom */
6262           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6263           for (o = oetf; o < oetr; ++o)
6264             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6265           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6266           /*   middle */
6267           for (i = 0; i < k - 1; ++i) {
6268             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6269             for (n = 0; n < k - 1; ++n)
6270               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6271             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6272           }
6273           /*   top */
6274           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6275           for (o = oetl - 1; o >= oetb; --o)
6276             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6277           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6278 
6279           foffset = offset;
6280         } else {
6281           PetscInt dof;
6282 
6283           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6284           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6285           foffset = offset;
6286         }
6287         break;
6288       default:
6289         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6290       }
6291     }
6292     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6293     /* Check permutation */
6294     {
6295       PetscInt *check;
6296 
6297       PetscCall(PetscMalloc1(size, &check));
6298       for (i = 0; i < size; ++i) {
6299         check[i] = -1;
6300         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6301       }
6302       for (i = 0; i < size; ++i) check[perm[i]] = i;
6303       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6304       PetscCall(PetscFree(check));
6305     }
6306     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6307     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6308       PetscInt *loc_perm;
6309       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6310       for (PetscInt i = 0; i < size; i++) {
6311         loc_perm[i]        = perm[i];
6312         loc_perm[size + i] = size + perm[i];
6313       }
6314       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6315     }
6316   }
6317   PetscFunctionReturn(PETSC_SUCCESS);
6318 }
6319 
6320 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6321 {
6322   PetscDS  prob;
6323   PetscInt depth, Nf, h;
6324   DMLabel  label;
6325 
6326   PetscFunctionBeginHot;
6327   PetscCall(DMGetDS(dm, &prob));
6328   Nf      = prob->Nf;
6329   label   = dm->depthLabel;
6330   *dspace = NULL;
6331   if (field < Nf) {
6332     PetscObject disc = prob->disc[field];
6333 
6334     if (disc->classid == PETSCFE_CLASSID) {
6335       PetscDualSpace dsp;
6336 
6337       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6338       PetscCall(DMLabelGetNumValues(label, &depth));
6339       PetscCall(DMLabelGetValue(label, point, &h));
6340       h = depth - 1 - h;
6341       if (h) {
6342         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6343       } else {
6344         *dspace = dsp;
6345       }
6346     }
6347   }
6348   PetscFunctionReturn(PETSC_SUCCESS);
6349 }
6350 
6351 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6352 {
6353   PetscScalar       *array;
6354   const PetscScalar *vArray;
6355   const PetscInt    *cone, *coneO;
6356   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6357 
6358   PetscFunctionBeginHot;
6359   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6360   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6361   PetscCall(DMPlexGetCone(dm, point, &cone));
6362   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6363   if (!values || !*values) {
6364     if ((point >= pStart) && (point < pEnd)) {
6365       PetscInt dof;
6366 
6367       PetscCall(PetscSectionGetDof(section, point, &dof));
6368       size += dof;
6369     }
6370     for (p = 0; p < numPoints; ++p) {
6371       const PetscInt cp = cone[p];
6372       PetscInt       dof;
6373 
6374       if ((cp < pStart) || (cp >= pEnd)) continue;
6375       PetscCall(PetscSectionGetDof(section, cp, &dof));
6376       size += dof;
6377     }
6378     if (!values) {
6379       if (csize) *csize = size;
6380       PetscFunctionReturn(PETSC_SUCCESS);
6381     }
6382     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6383   } else {
6384     array = *values;
6385   }
6386   size = 0;
6387   PetscCall(VecGetArrayRead(v, &vArray));
6388   if ((point >= pStart) && (point < pEnd)) {
6389     PetscInt           dof, off, d;
6390     const PetscScalar *varr;
6391 
6392     PetscCall(PetscSectionGetDof(section, point, &dof));
6393     PetscCall(PetscSectionGetOffset(section, point, &off));
6394     varr = PetscSafePointerPlusOffset(vArray, off);
6395     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6396     size += dof;
6397   }
6398   for (p = 0; p < numPoints; ++p) {
6399     const PetscInt     cp = cone[p];
6400     PetscInt           o  = coneO[p];
6401     PetscInt           dof, off, d;
6402     const PetscScalar *varr;
6403 
6404     if ((cp < pStart) || (cp >= pEnd)) continue;
6405     PetscCall(PetscSectionGetDof(section, cp, &dof));
6406     PetscCall(PetscSectionGetOffset(section, cp, &off));
6407     varr = PetscSafePointerPlusOffset(vArray, off);
6408     if (o >= 0) {
6409       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6410     } else {
6411       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6412     }
6413     size += dof;
6414   }
6415   PetscCall(VecRestoreArrayRead(v, &vArray));
6416   if (!*values) {
6417     if (csize) *csize = size;
6418     *values = array;
6419   } else {
6420     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6421     *csize = size;
6422   }
6423   PetscFunctionReturn(PETSC_SUCCESS);
6424 }
6425 
6426 /* Compress out points not in the section */
6427 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6428 {
6429   const PetscInt np = *numPoints;
6430   PetscInt       pStart, pEnd, p, q;
6431 
6432   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6433   for (p = 0, q = 0; p < np; ++p) {
6434     const PetscInt r = points[p * 2];
6435     if ((r >= pStart) && (r < pEnd)) {
6436       points[q * 2]     = r;
6437       points[q * 2 + 1] = points[p * 2 + 1];
6438       ++q;
6439     }
6440   }
6441   *numPoints = q;
6442   return PETSC_SUCCESS;
6443 }
6444 
6445 /* Compressed closure does not apply closure permutation */
6446 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6447 {
6448   const PetscInt *cla = NULL;
6449   PetscInt        np, *pts = NULL;
6450 
6451   PetscFunctionBeginHot;
6452   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6453   if (!ornt && *clPoints) {
6454     PetscInt dof, off;
6455 
6456     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6457     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6458     PetscCall(ISGetIndices(*clPoints, &cla));
6459     np  = dof / 2;
6460     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6461   } else {
6462     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6463     PetscCall(CompressPoints_Private(section, &np, pts));
6464   }
6465   *numPoints = np;
6466   *points    = pts;
6467   *clp       = cla;
6468   PetscFunctionReturn(PETSC_SUCCESS);
6469 }
6470 
6471 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6472 {
6473   PetscFunctionBeginHot;
6474   if (!*clPoints) {
6475     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6476   } else {
6477     PetscCall(ISRestoreIndices(*clPoints, clp));
6478   }
6479   *numPoints = 0;
6480   *points    = NULL;
6481   *clSec     = NULL;
6482   *clPoints  = NULL;
6483   *clp       = NULL;
6484   PetscFunctionReturn(PETSC_SUCCESS);
6485 }
6486 
6487 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6488 {
6489   PetscInt            offset = 0, p;
6490   const PetscInt    **perms  = NULL;
6491   const PetscScalar **flips  = NULL;
6492 
6493   PetscFunctionBeginHot;
6494   *size = 0;
6495   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6496   for (p = 0; p < numPoints; p++) {
6497     const PetscInt     point = points[2 * p];
6498     const PetscInt    *perm  = perms ? perms[p] : NULL;
6499     const PetscScalar *flip  = flips ? flips[p] : NULL;
6500     PetscInt           dof, off, d;
6501     const PetscScalar *varr;
6502 
6503     PetscCall(PetscSectionGetDof(section, point, &dof));
6504     PetscCall(PetscSectionGetOffset(section, point, &off));
6505     varr = PetscSafePointerPlusOffset(vArray, off);
6506     if (clperm) {
6507       if (perm) {
6508         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6509       } else {
6510         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6511       }
6512       if (flip) {
6513         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6514       }
6515     } else {
6516       if (perm) {
6517         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6518       } else {
6519         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6520       }
6521       if (flip) {
6522         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6523       }
6524     }
6525     offset += dof;
6526   }
6527   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6528   *size = offset;
6529   PetscFunctionReturn(PETSC_SUCCESS);
6530 }
6531 
6532 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[])
6533 {
6534   PetscInt offset = 0, f;
6535 
6536   PetscFunctionBeginHot;
6537   *size = 0;
6538   for (f = 0; f < numFields; ++f) {
6539     PetscInt            p;
6540     const PetscInt    **perms = NULL;
6541     const PetscScalar **flips = NULL;
6542 
6543     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6544     for (p = 0; p < numPoints; p++) {
6545       const PetscInt     point = points[2 * p];
6546       PetscInt           fdof, foff, b;
6547       const PetscScalar *varr;
6548       const PetscInt    *perm = perms ? perms[p] : NULL;
6549       const PetscScalar *flip = flips ? flips[p] : NULL;
6550 
6551       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6552       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6553       varr = &vArray[foff];
6554       if (clperm) {
6555         if (perm) {
6556           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6557         } else {
6558           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6559         }
6560         if (flip) {
6561           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6562         }
6563       } else {
6564         if (perm) {
6565           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6566         } else {
6567           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6568         }
6569         if (flip) {
6570           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6571         }
6572       }
6573       offset += fdof;
6574     }
6575     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6576   }
6577   *size = offset;
6578   PetscFunctionReturn(PETSC_SUCCESS);
6579 }
6580 
6581 /*@C
6582   DMPlexVecGetOrientedClosure - Get an array of the values on the closure of 'point' with a given orientation, optionally applying the closure permutation.
6583 
6584   Not collective
6585 
6586   Input Parameters:
6587 + dm        - The `DM`
6588 . section   - The section describing the layout in `v`, or `NULL` to use the default section
6589 . useClPerm - Flag for whether the provided closure permutation should be applied to the values
6590 . v         - The local vector
6591 . point     - The point in the `DM`
6592 - ornt      - The orientation of the cell, an integer giving the prescription for cone traversal. Typically, this will be 0.
6593 
6594   Input/Output Parameters:
6595 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6596 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6597            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6598 
6599   Level: advanced
6600 
6601   Notes:
6602   `DMPlexVecGetOrientedClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6603   calling function. This is because `DMPlexVecGetOrientedClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6604   assembly function, and a user may already have allocated storage for this operation.
6605 
6606   Fortran Notes:
6607   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6608   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6609 
6610   `values` must be declared with
6611 .vb
6612   PetscScalar,dimension(:),pointer   :: values
6613 .ve
6614   and it will be allocated internally by PETSc to hold the values returned
6615 
6616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexGetCellCoordinates()`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`
6617 @*/
6618 PetscErrorCode DMPlexVecGetOrientedClosure(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6619 {
6620   PetscSection    clSection;
6621   IS              clPoints;
6622   PetscInt       *points = NULL;
6623   const PetscInt *clp, *perm = NULL;
6624   PetscInt        depth, numFields, numPoints, asize;
6625 
6626   PetscFunctionBeginHot;
6627   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6628   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6629   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6630   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6631   PetscCall(DMPlexGetDepth(dm, &depth));
6632   PetscCall(PetscSectionGetNumFields(section, &numFields));
6633   if (depth == 1 && numFields < 2) {
6634     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6635     PetscFunctionReturn(PETSC_SUCCESS);
6636   }
6637   /* Get points */
6638   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6639   /* Get sizes */
6640   asize = 0;
6641   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6642     PetscInt dof;
6643     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6644     asize += dof;
6645   }
6646   if (values) {
6647     const PetscScalar *vArray;
6648     PetscInt           size;
6649 
6650     if (*values) {
6651       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);
6652     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6653     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6654     PetscCall(VecGetArrayRead(v, &vArray));
6655     /* Get values */
6656     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6657     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6658     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6659     /* Cleanup array */
6660     PetscCall(VecRestoreArrayRead(v, &vArray));
6661   }
6662   if (csize) *csize = asize;
6663   /* Cleanup points */
6664   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6665   PetscFunctionReturn(PETSC_SUCCESS);
6666 }
6667 
6668 /*@C
6669   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6670 
6671   Not collective
6672 
6673   Input Parameters:
6674 + dm      - The `DM`
6675 . section - The section describing the layout in `v`, or `NULL` to use the default section
6676 . v       - The local vector
6677 - point   - The point in the `DM`
6678 
6679   Input/Output Parameters:
6680 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6681 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6682            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6683 
6684   Level: intermediate
6685 
6686   Notes:
6687   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6688   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6689   assembly function, and a user may already have allocated storage for this operation.
6690 
6691   A typical use could be
6692 .vb
6693    values = NULL;
6694    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6695    for (cl = 0; cl < clSize; ++cl) {
6696      <Compute on closure>
6697    }
6698    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6699 .ve
6700   or
6701 .vb
6702    PetscMalloc1(clMaxSize, &values);
6703    for (p = pStart; p < pEnd; ++p) {
6704      clSize = clMaxSize;
6705      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6706      for (cl = 0; cl < clSize; ++cl) {
6707        <Compute on closure>
6708      }
6709    }
6710    PetscFree(values);
6711 .ve
6712 
6713   Fortran Notes:
6714   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6715   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6716 
6717   `values` must be declared with
6718 .vb
6719   PetscScalar,dimension(:),pointer   :: values
6720 .ve
6721   and it will be allocated internally by PETSc to hold the values returned
6722 
6723 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6724 @*/
6725 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6726 {
6727   PetscFunctionBeginHot;
6728   PetscCall(DMPlexVecGetOrientedClosure(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6729   PetscFunctionReturn(PETSC_SUCCESS);
6730 }
6731 
6732 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6733 {
6734   DMLabel            depthLabel;
6735   PetscSection       clSection;
6736   IS                 clPoints;
6737   PetscScalar       *array;
6738   const PetscScalar *vArray;
6739   PetscInt          *points = NULL;
6740   const PetscInt    *clp, *perm = NULL;
6741   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6742 
6743   PetscFunctionBeginHot;
6744   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6745   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6746   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6747   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6748   PetscCall(DMPlexGetDepth(dm, &mdepth));
6749   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6750   PetscCall(PetscSectionGetNumFields(section, &numFields));
6751   if (mdepth == 1 && numFields < 2) {
6752     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6753     PetscFunctionReturn(PETSC_SUCCESS);
6754   }
6755   /* Get points */
6756   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6757   for (clsize = 0, p = 0; p < Np; p++) {
6758     PetscInt dof;
6759     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6760     clsize += dof;
6761   }
6762   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6763   /* Filter points */
6764   for (p = 0; p < numPoints * 2; p += 2) {
6765     PetscInt dep;
6766 
6767     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6768     if (dep != depth) continue;
6769     points[Np * 2 + 0] = points[p];
6770     points[Np * 2 + 1] = points[p + 1];
6771     ++Np;
6772   }
6773   /* Get array */
6774   if (!values || !*values) {
6775     PetscInt asize = 0, dof;
6776 
6777     for (p = 0; p < Np * 2; p += 2) {
6778       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6779       asize += dof;
6780     }
6781     if (!values) {
6782       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6783       if (csize) *csize = asize;
6784       PetscFunctionReturn(PETSC_SUCCESS);
6785     }
6786     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6787   } else {
6788     array = *values;
6789   }
6790   PetscCall(VecGetArrayRead(v, &vArray));
6791   /* Get values */
6792   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6793   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6794   /* Cleanup points */
6795   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6796   /* Cleanup array */
6797   PetscCall(VecRestoreArrayRead(v, &vArray));
6798   if (!*values) {
6799     if (csize) *csize = size;
6800     *values = array;
6801   } else {
6802     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6803     *csize = size;
6804   }
6805   PetscFunctionReturn(PETSC_SUCCESS);
6806 }
6807 
6808 /*@C
6809   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6810 
6811   Not collective
6812 
6813   Input Parameters:
6814 + dm      - The `DM`
6815 . section - The section describing the layout in `v`, or `NULL` to use the default section
6816 . v       - The local vector
6817 . point   - The point in the `DM`
6818 . csize   - The number of values in the closure, or `NULL`
6819 - values  - The array of values
6820 
6821   Level: intermediate
6822 
6823   Note:
6824   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6825 
6826   Fortran Note:
6827   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6828   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6829 
6830 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6831 @*/
6832 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6833 {
6834   PetscInt size = 0;
6835 
6836   PetscFunctionBegin;
6837   /* Should work without recalculating size */
6838   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6839   *values = NULL;
6840   PetscFunctionReturn(PETSC_SUCCESS);
6841 }
6842 
6843 static inline void add(PetscScalar *x, PetscScalar y)
6844 {
6845   *x += y;
6846 }
6847 static inline void insert(PetscScalar *x, PetscScalar y)
6848 {
6849   *x = y;
6850 }
6851 
6852 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[])
6853 {
6854   PetscInt        cdof;  /* The number of constraints on this point */
6855   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6856   PetscScalar    *a;
6857   PetscInt        off, cind = 0, k;
6858 
6859   PetscFunctionBegin;
6860   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6861   PetscCall(PetscSectionGetOffset(section, point, &off));
6862   a = &array[off];
6863   if (!cdof || setBC) {
6864     if (clperm) {
6865       if (perm) {
6866         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6867       } else {
6868         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6869       }
6870     } else {
6871       if (perm) {
6872         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6873       } else {
6874         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6875       }
6876     }
6877   } else {
6878     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6879     if (clperm) {
6880       if (perm) {
6881         for (k = 0; k < dof; ++k) {
6882           if ((cind < cdof) && (k == cdofs[cind])) {
6883             ++cind;
6884             continue;
6885           }
6886           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6887         }
6888       } else {
6889         for (k = 0; k < dof; ++k) {
6890           if ((cind < cdof) && (k == cdofs[cind])) {
6891             ++cind;
6892             continue;
6893           }
6894           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6895         }
6896       }
6897     } else {
6898       if (perm) {
6899         for (k = 0; k < dof; ++k) {
6900           if ((cind < cdof) && (k == cdofs[cind])) {
6901             ++cind;
6902             continue;
6903           }
6904           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6905         }
6906       } else {
6907         for (k = 0; k < dof; ++k) {
6908           if ((cind < cdof) && (k == cdofs[cind])) {
6909             ++cind;
6910             continue;
6911           }
6912           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6913         }
6914       }
6915     }
6916   }
6917   PetscFunctionReturn(PETSC_SUCCESS);
6918 }
6919 
6920 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[])
6921 {
6922   PetscInt        cdof;  /* The number of constraints on this point */
6923   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6924   PetscScalar    *a;
6925   PetscInt        off, cind = 0, k;
6926 
6927   PetscFunctionBegin;
6928   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6929   PetscCall(PetscSectionGetOffset(section, point, &off));
6930   a = &array[off];
6931   if (cdof) {
6932     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6933     if (clperm) {
6934       if (perm) {
6935         for (k = 0; k < dof; ++k) {
6936           if ((cind < cdof) && (k == cdofs[cind])) {
6937             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6938             cind++;
6939           }
6940         }
6941       } else {
6942         for (k = 0; k < dof; ++k) {
6943           if ((cind < cdof) && (k == cdofs[cind])) {
6944             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6945             cind++;
6946           }
6947         }
6948       }
6949     } else {
6950       if (perm) {
6951         for (k = 0; k < dof; ++k) {
6952           if ((cind < cdof) && (k == cdofs[cind])) {
6953             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6954             cind++;
6955           }
6956         }
6957       } else {
6958         for (k = 0; k < dof; ++k) {
6959           if ((cind < cdof) && (k == cdofs[cind])) {
6960             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6961             cind++;
6962           }
6963         }
6964       }
6965     }
6966   }
6967   PetscFunctionReturn(PETSC_SUCCESS);
6968 }
6969 
6970 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[])
6971 {
6972   PetscScalar    *a;
6973   PetscInt        fdof, foff, fcdof, foffset = *offset;
6974   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6975   PetscInt        cind = 0, b;
6976 
6977   PetscFunctionBegin;
6978   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6979   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6980   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6981   a = &array[foff];
6982   if (!fcdof || setBC) {
6983     if (clperm) {
6984       if (perm) {
6985         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6986       } else {
6987         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6988       }
6989     } else {
6990       if (perm) {
6991         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6992       } else {
6993         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6994       }
6995     }
6996   } else {
6997     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6998     if (clperm) {
6999       if (perm) {
7000         for (b = 0; b < fdof; b++) {
7001           if ((cind < fcdof) && (b == fcdofs[cind])) {
7002             ++cind;
7003             continue;
7004           }
7005           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7006         }
7007       } else {
7008         for (b = 0; b < fdof; b++) {
7009           if ((cind < fcdof) && (b == fcdofs[cind])) {
7010             ++cind;
7011             continue;
7012           }
7013           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7014         }
7015       }
7016     } else {
7017       if (perm) {
7018         for (b = 0; b < fdof; b++) {
7019           if ((cind < fcdof) && (b == fcdofs[cind])) {
7020             ++cind;
7021             continue;
7022           }
7023           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7024         }
7025       } else {
7026         for (b = 0; b < fdof; b++) {
7027           if ((cind < fcdof) && (b == fcdofs[cind])) {
7028             ++cind;
7029             continue;
7030           }
7031           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7032         }
7033       }
7034     }
7035   }
7036   *offset += fdof;
7037   PetscFunctionReturn(PETSC_SUCCESS);
7038 }
7039 
7040 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[])
7041 {
7042   PetscScalar    *a;
7043   PetscInt        fdof, foff, fcdof, foffset = *offset;
7044   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7045   PetscInt        Nc, cind = 0, ncind = 0, b;
7046   PetscBool       ncSet, fcSet;
7047 
7048   PetscFunctionBegin;
7049   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
7050   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7051   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
7052   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
7053   a = &array[foff];
7054   if (fcdof) {
7055     /* We just override fcdof and fcdofs with Ncc and comps */
7056     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7057     if (clperm) {
7058       if (perm) {
7059         if (comps) {
7060           for (b = 0; b < fdof; b++) {
7061             ncSet = fcSet = PETSC_FALSE;
7062             if (b % Nc == comps[ncind]) {
7063               ncind = (ncind + 1) % Ncc;
7064               ncSet = PETSC_TRUE;
7065             }
7066             if ((cind < fcdof) && (b == fcdofs[cind])) {
7067               ++cind;
7068               fcSet = PETSC_TRUE;
7069             }
7070             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7071           }
7072         } else {
7073           for (b = 0; b < fdof; b++) {
7074             if ((cind < fcdof) && (b == fcdofs[cind])) {
7075               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7076               ++cind;
7077             }
7078           }
7079         }
7080       } else {
7081         if (comps) {
7082           for (b = 0; b < fdof; b++) {
7083             ncSet = fcSet = PETSC_FALSE;
7084             if (b % Nc == comps[ncind]) {
7085               ncind = (ncind + 1) % Ncc;
7086               ncSet = PETSC_TRUE;
7087             }
7088             if ((cind < fcdof) && (b == fcdofs[cind])) {
7089               ++cind;
7090               fcSet = PETSC_TRUE;
7091             }
7092             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7093           }
7094         } else {
7095           for (b = 0; b < fdof; b++) {
7096             if ((cind < fcdof) && (b == fcdofs[cind])) {
7097               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7098               ++cind;
7099             }
7100           }
7101         }
7102       }
7103     } else {
7104       if (perm) {
7105         if (comps) {
7106           for (b = 0; b < fdof; b++) {
7107             ncSet = fcSet = PETSC_FALSE;
7108             if (b % Nc == comps[ncind]) {
7109               ncind = (ncind + 1) % Ncc;
7110               ncSet = PETSC_TRUE;
7111             }
7112             if ((cind < fcdof) && (b == fcdofs[cind])) {
7113               ++cind;
7114               fcSet = PETSC_TRUE;
7115             }
7116             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7117           }
7118         } else {
7119           for (b = 0; b < fdof; b++) {
7120             if ((cind < fcdof) && (b == fcdofs[cind])) {
7121               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7122               ++cind;
7123             }
7124           }
7125         }
7126       } else {
7127         if (comps) {
7128           for (b = 0; b < fdof; b++) {
7129             ncSet = fcSet = PETSC_FALSE;
7130             if (b % Nc == comps[ncind]) {
7131               ncind = (ncind + 1) % Ncc;
7132               ncSet = PETSC_TRUE;
7133             }
7134             if ((cind < fcdof) && (b == fcdofs[cind])) {
7135               ++cind;
7136               fcSet = PETSC_TRUE;
7137             }
7138             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7139           }
7140         } else {
7141           for (b = 0; b < fdof; b++) {
7142             if ((cind < fcdof) && (b == fcdofs[cind])) {
7143               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7144               ++cind;
7145             }
7146           }
7147         }
7148       }
7149     }
7150   }
7151   *offset += fdof;
7152   PetscFunctionReturn(PETSC_SUCCESS);
7153 }
7154 
7155 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7156 {
7157   PetscScalar    *array;
7158   const PetscInt *cone, *coneO;
7159   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7160 
7161   PetscFunctionBeginHot;
7162   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7163   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7164   PetscCall(DMPlexGetCone(dm, point, &cone));
7165   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7166   PetscCall(VecGetArray(v, &array));
7167   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7168     const PetscInt cp = !p ? point : cone[p - 1];
7169     const PetscInt o  = !p ? 0 : coneO[p - 1];
7170 
7171     if ((cp < pStart) || (cp >= pEnd)) {
7172       dof = 0;
7173       continue;
7174     }
7175     PetscCall(PetscSectionGetDof(section, cp, &dof));
7176     /* ADD_VALUES */
7177     {
7178       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7179       PetscScalar    *a;
7180       PetscInt        cdof, coff, cind = 0, k;
7181 
7182       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7183       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7184       a = &array[coff];
7185       if (!cdof) {
7186         if (o >= 0) {
7187           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7188         } else {
7189           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7190         }
7191       } else {
7192         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7193         if (o >= 0) {
7194           for (k = 0; k < dof; ++k) {
7195             if ((cind < cdof) && (k == cdofs[cind])) {
7196               ++cind;
7197               continue;
7198             }
7199             a[k] += values[off + k];
7200           }
7201         } else {
7202           for (k = 0; k < dof; ++k) {
7203             if ((cind < cdof) && (k == cdofs[cind])) {
7204               ++cind;
7205               continue;
7206             }
7207             a[k] += values[off + dof - k - 1];
7208           }
7209         }
7210       }
7211     }
7212   }
7213   PetscCall(VecRestoreArray(v, &array));
7214   PetscFunctionReturn(PETSC_SUCCESS);
7215 }
7216 
7217 /*@C
7218   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7219 
7220   Not collective
7221 
7222   Input Parameters:
7223 + dm      - The `DM`
7224 . section - The section describing the layout in `v`, or `NULL` to use the default section
7225 . v       - The local vector
7226 . point   - The point in the `DM`
7227 . values  - The array of values
7228 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7229             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7230 
7231   Level: intermediate
7232 
7233   Note:
7234   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7235 
7236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7237 @*/
7238 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7239 {
7240   PetscSection    clSection;
7241   IS              clPoints;
7242   PetscScalar    *array;
7243   PetscInt       *points = NULL;
7244   const PetscInt *clp, *clperm = NULL;
7245   PetscInt        depth, numFields, numPoints, p, clsize;
7246 
7247   PetscFunctionBeginHot;
7248   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7249   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7250   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7251   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7252   PetscCall(DMPlexGetDepth(dm, &depth));
7253   PetscCall(PetscSectionGetNumFields(section, &numFields));
7254   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7255     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7256     PetscFunctionReturn(PETSC_SUCCESS);
7257   }
7258   /* Get points */
7259   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7260   for (clsize = 0, p = 0; p < numPoints; p++) {
7261     PetscInt dof;
7262     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7263     clsize += dof;
7264   }
7265   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7266   /* Get array */
7267   PetscCall(VecGetArray(v, &array));
7268   /* Get values */
7269   if (numFields > 0) {
7270     PetscInt offset = 0, f;
7271     for (f = 0; f < numFields; ++f) {
7272       const PetscInt    **perms = NULL;
7273       const PetscScalar **flips = NULL;
7274 
7275       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7276       switch (mode) {
7277       case INSERT_VALUES:
7278         for (p = 0; p < numPoints; p++) {
7279           const PetscInt     point = points[2 * p];
7280           const PetscInt    *perm  = perms ? perms[p] : NULL;
7281           const PetscScalar *flip  = flips ? flips[p] : NULL;
7282           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7283         }
7284         break;
7285       case INSERT_ALL_VALUES:
7286         for (p = 0; p < numPoints; p++) {
7287           const PetscInt     point = points[2 * p];
7288           const PetscInt    *perm  = perms ? perms[p] : NULL;
7289           const PetscScalar *flip  = flips ? flips[p] : NULL;
7290           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7291         }
7292         break;
7293       case INSERT_BC_VALUES:
7294         for (p = 0; p < numPoints; p++) {
7295           const PetscInt     point = points[2 * p];
7296           const PetscInt    *perm  = perms ? perms[p] : NULL;
7297           const PetscScalar *flip  = flips ? flips[p] : NULL;
7298           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7299         }
7300         break;
7301       case ADD_VALUES:
7302         for (p = 0; p < numPoints; p++) {
7303           const PetscInt     point = points[2 * p];
7304           const PetscInt    *perm  = perms ? perms[p] : NULL;
7305           const PetscScalar *flip  = flips ? flips[p] : NULL;
7306           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7307         }
7308         break;
7309       case ADD_ALL_VALUES:
7310         for (p = 0; p < numPoints; p++) {
7311           const PetscInt     point = points[2 * p];
7312           const PetscInt    *perm  = perms ? perms[p] : NULL;
7313           const PetscScalar *flip  = flips ? flips[p] : NULL;
7314           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7315         }
7316         break;
7317       case ADD_BC_VALUES:
7318         for (p = 0; p < numPoints; p++) {
7319           const PetscInt     point = points[2 * p];
7320           const PetscInt    *perm  = perms ? perms[p] : NULL;
7321           const PetscScalar *flip  = flips ? flips[p] : NULL;
7322           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7323         }
7324         break;
7325       default:
7326         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7327       }
7328       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7329     }
7330   } else {
7331     PetscInt            dof, off;
7332     const PetscInt    **perms = NULL;
7333     const PetscScalar **flips = NULL;
7334 
7335     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7336     switch (mode) {
7337     case INSERT_VALUES:
7338       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7339         const PetscInt     point = points[2 * p];
7340         const PetscInt    *perm  = perms ? perms[p] : NULL;
7341         const PetscScalar *flip  = flips ? flips[p] : NULL;
7342         PetscCall(PetscSectionGetDof(section, point, &dof));
7343         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7344       }
7345       break;
7346     case INSERT_ALL_VALUES:
7347       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7348         const PetscInt     point = points[2 * p];
7349         const PetscInt    *perm  = perms ? perms[p] : NULL;
7350         const PetscScalar *flip  = flips ? flips[p] : NULL;
7351         PetscCall(PetscSectionGetDof(section, point, &dof));
7352         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7353       }
7354       break;
7355     case INSERT_BC_VALUES:
7356       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7357         const PetscInt     point = points[2 * p];
7358         const PetscInt    *perm  = perms ? perms[p] : NULL;
7359         const PetscScalar *flip  = flips ? flips[p] : NULL;
7360         PetscCall(PetscSectionGetDof(section, point, &dof));
7361         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7362       }
7363       break;
7364     case ADD_VALUES:
7365       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7366         const PetscInt     point = points[2 * p];
7367         const PetscInt    *perm  = perms ? perms[p] : NULL;
7368         const PetscScalar *flip  = flips ? flips[p] : NULL;
7369         PetscCall(PetscSectionGetDof(section, point, &dof));
7370         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7371       }
7372       break;
7373     case ADD_ALL_VALUES:
7374       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7375         const PetscInt     point = points[2 * p];
7376         const PetscInt    *perm  = perms ? perms[p] : NULL;
7377         const PetscScalar *flip  = flips ? flips[p] : NULL;
7378         PetscCall(PetscSectionGetDof(section, point, &dof));
7379         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7380       }
7381       break;
7382     case ADD_BC_VALUES:
7383       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7384         const PetscInt     point = points[2 * p];
7385         const PetscInt    *perm  = perms ? perms[p] : NULL;
7386         const PetscScalar *flip  = flips ? flips[p] : NULL;
7387         PetscCall(PetscSectionGetDof(section, point, &dof));
7388         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7389       }
7390       break;
7391     default:
7392       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7393     }
7394     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7395   }
7396   /* Cleanup points */
7397   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7398   /* Cleanup array */
7399   PetscCall(VecRestoreArray(v, &array));
7400   PetscFunctionReturn(PETSC_SUCCESS);
7401 }
7402 
7403 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7404 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7405 {
7406   PetscFunctionBegin;
7407   *contains = PETSC_TRUE;
7408   if (label) {
7409     PetscInt fdof;
7410 
7411     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7412     if (!*contains) {
7413       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7414       *offset += fdof;
7415       PetscFunctionReturn(PETSC_SUCCESS);
7416     }
7417   }
7418   PetscFunctionReturn(PETSC_SUCCESS);
7419 }
7420 
7421 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7422 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)
7423 {
7424   PetscSection    clSection;
7425   IS              clPoints;
7426   PetscScalar    *array;
7427   PetscInt       *points = NULL;
7428   const PetscInt *clp;
7429   PetscInt        numFields, numPoints, p;
7430   PetscInt        offset = 0, f;
7431 
7432   PetscFunctionBeginHot;
7433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7434   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7435   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7436   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7437   PetscCall(PetscSectionGetNumFields(section, &numFields));
7438   /* Get points */
7439   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7440   /* Get array */
7441   PetscCall(VecGetArray(v, &array));
7442   /* Get values */
7443   for (f = 0; f < numFields; ++f) {
7444     const PetscInt    **perms = NULL;
7445     const PetscScalar **flips = NULL;
7446     PetscBool           contains;
7447 
7448     if (!fieldActive[f]) {
7449       for (p = 0; p < numPoints * 2; p += 2) {
7450         PetscInt fdof;
7451         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7452         offset += fdof;
7453       }
7454       continue;
7455     }
7456     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7457     switch (mode) {
7458     case INSERT_VALUES:
7459       for (p = 0; p < numPoints; p++) {
7460         const PetscInt     point = points[2 * p];
7461         const PetscInt    *perm  = perms ? perms[p] : NULL;
7462         const PetscScalar *flip  = flips ? flips[p] : NULL;
7463         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7464         if (!contains) continue;
7465         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7466       }
7467       break;
7468     case INSERT_ALL_VALUES:
7469       for (p = 0; p < numPoints; p++) {
7470         const PetscInt     point = points[2 * p];
7471         const PetscInt    *perm  = perms ? perms[p] : NULL;
7472         const PetscScalar *flip  = flips ? flips[p] : NULL;
7473         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7474         if (!contains) continue;
7475         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7476       }
7477       break;
7478     case INSERT_BC_VALUES:
7479       for (p = 0; p < numPoints; p++) {
7480         const PetscInt     point = points[2 * p];
7481         const PetscInt    *perm  = perms ? perms[p] : NULL;
7482         const PetscScalar *flip  = flips ? flips[p] : NULL;
7483         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7484         if (!contains) continue;
7485         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7486       }
7487       break;
7488     case ADD_VALUES:
7489       for (p = 0; p < numPoints; p++) {
7490         const PetscInt     point = points[2 * p];
7491         const PetscInt    *perm  = perms ? perms[p] : NULL;
7492         const PetscScalar *flip  = flips ? flips[p] : NULL;
7493         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7494         if (!contains) continue;
7495         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7496       }
7497       break;
7498     case ADD_ALL_VALUES:
7499       for (p = 0; p < numPoints; p++) {
7500         const PetscInt     point = points[2 * p];
7501         const PetscInt    *perm  = perms ? perms[p] : NULL;
7502         const PetscScalar *flip  = flips ? flips[p] : NULL;
7503         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7504         if (!contains) continue;
7505         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7506       }
7507       break;
7508     default:
7509       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7510     }
7511     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7512   }
7513   /* Cleanup points */
7514   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7515   /* Cleanup array */
7516   PetscCall(VecRestoreArray(v, &array));
7517   PetscFunctionReturn(PETSC_SUCCESS);
7518 }
7519 
7520 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7521 {
7522   PetscMPIInt rank;
7523   PetscInt    i, j;
7524 
7525   PetscFunctionBegin;
7526   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7527   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7528   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7529   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7530   numCIndices = numCIndices ? numCIndices : numRIndices;
7531   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7532   for (i = 0; i < numRIndices; i++) {
7533     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7534     for (j = 0; j < numCIndices; j++) {
7535 #if defined(PETSC_USE_COMPLEX)
7536       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7537 #else
7538       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7539 #endif
7540     }
7541     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7542   }
7543   PetscFunctionReturn(PETSC_SUCCESS);
7544 }
7545 
7546 /*
7547   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7548 
7549   Input Parameters:
7550 + section - The section for this data layout
7551 . islocal - Is the section (and thus indices being requested) local or global?
7552 . point   - The point contributing dofs with these indices
7553 . off     - The global offset of this point
7554 . loff    - The local offset of each field
7555 . setBC   - The flag determining whether to include indices of boundary values
7556 . perm    - A permutation of the dofs on this point, or NULL
7557 - indperm - A permutation of the entire indices array, or NULL
7558 
7559   Output Parameter:
7560 . indices - Indices for dofs on this point
7561 
7562   Level: developer
7563 
7564   Note: The indices could be local or global, depending on the value of 'off'.
7565 */
7566 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7567 {
7568   PetscInt        dof;   /* The number of unknowns on this point */
7569   PetscInt        cdof;  /* The number of constraints on this point */
7570   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7571   PetscInt        cind = 0, k;
7572 
7573   PetscFunctionBegin;
7574   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7575   PetscCall(PetscSectionGetDof(section, point, &dof));
7576   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7577   if (!cdof || setBC) {
7578     for (k = 0; k < dof; ++k) {
7579       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7580       const PetscInt ind    = indperm ? indperm[preind] : preind;
7581 
7582       indices[ind] = off + k;
7583     }
7584   } else {
7585     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7586     for (k = 0; k < dof; ++k) {
7587       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7588       const PetscInt ind    = indperm ? indperm[preind] : preind;
7589 
7590       if ((cind < cdof) && (k == cdofs[cind])) {
7591         /* Insert check for returning constrained indices */
7592         indices[ind] = -(off + k + 1);
7593         ++cind;
7594       } else {
7595         indices[ind] = off + k - (islocal ? 0 : cind);
7596       }
7597     }
7598   }
7599   *loff += dof;
7600   PetscFunctionReturn(PETSC_SUCCESS);
7601 }
7602 
7603 /*
7604  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7605 
7606  Input Parameters:
7607 + section - a section (global or local)
7608 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7609 . point - point within section
7610 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7611 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7612 . setBC - identify constrained (boundary condition) points via involution.
7613 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7614 . permsoff - offset
7615 - indperm - index permutation
7616 
7617  Output Parameter:
7618 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7619 . indices - array to hold indices (as defined by section) of each dof associated with point
7620 
7621  Notes:
7622  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7623  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7624  in the local vector.
7625 
7626  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7627  significant).  It is invalid to call with a global section and setBC=true.
7628 
7629  Developer Note:
7630  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7631  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7632  offset could be obtained from the section instead of passing it explicitly as we do now.
7633 
7634  Example:
7635  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7636  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7637  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7638  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.
7639 
7640  Level: developer
7641 */
7642 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[])
7643 {
7644   PetscInt numFields, foff, f;
7645 
7646   PetscFunctionBegin;
7647   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7648   PetscCall(PetscSectionGetNumFields(section, &numFields));
7649   for (f = 0, foff = 0; f < numFields; ++f) {
7650     PetscInt        fdof, cfdof;
7651     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7652     PetscInt        cind = 0, b;
7653     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7654 
7655     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7656     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7657     if (!cfdof || setBC) {
7658       for (b = 0; b < fdof; ++b) {
7659         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7660         const PetscInt ind    = indperm ? indperm[preind] : preind;
7661 
7662         indices[ind] = off + foff + b;
7663       }
7664     } else {
7665       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7666       for (b = 0; b < fdof; ++b) {
7667         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7668         const PetscInt ind    = indperm ? indperm[preind] : preind;
7669 
7670         if ((cind < cfdof) && (b == fcdofs[cind])) {
7671           indices[ind] = -(off + foff + b + 1);
7672           ++cind;
7673         } else {
7674           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7675         }
7676       }
7677     }
7678     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7679     foffs[f] += fdof;
7680   }
7681   PetscFunctionReturn(PETSC_SUCCESS);
7682 }
7683 
7684 /*
7685   This version believes the globalSection offsets for each field, rather than just the point offset
7686 
7687  . foffs - The offset into 'indices' for each field, since it is segregated by field
7688 
7689  Notes:
7690  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7691  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7692 */
7693 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7694 {
7695   PetscInt numFields, foff, f;
7696 
7697   PetscFunctionBegin;
7698   PetscCall(PetscSectionGetNumFields(section, &numFields));
7699   for (f = 0; f < numFields; ++f) {
7700     PetscInt        fdof, cfdof;
7701     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7702     PetscInt        cind = 0, b;
7703     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7704 
7705     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7706     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7707     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7708     if (!cfdof) {
7709       for (b = 0; b < fdof; ++b) {
7710         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7711         const PetscInt ind    = indperm ? indperm[preind] : preind;
7712 
7713         indices[ind] = foff + b;
7714       }
7715     } else {
7716       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7717       for (b = 0; b < fdof; ++b) {
7718         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7719         const PetscInt ind    = indperm ? indperm[preind] : preind;
7720 
7721         if ((cind < cfdof) && (b == fcdofs[cind])) {
7722           indices[ind] = -(foff + b + 1);
7723           ++cind;
7724         } else {
7725           indices[ind] = foff + b - cind;
7726         }
7727       }
7728     }
7729     foffs[f] += fdof;
7730   }
7731   PetscFunctionReturn(PETSC_SUCCESS);
7732 }
7733 
7734 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7735 {
7736   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7737 
7738   PetscFunctionBegin;
7739   PetscCall(PetscSectionGetNumFields(section, &numFields));
7740   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7741   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7742   for (PetscInt p = 0; p < nPoints; p++) {
7743     PetscInt     b       = pnts[2 * p];
7744     PetscInt     bSecDof = 0, bOff;
7745     PetscInt     cSecDof = 0;
7746     PetscSection indices_section;
7747 
7748     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7749     if (!bSecDof) continue;
7750     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7751     indices_section = cSecDof > 0 ? cSec : section;
7752     if (numFields) {
7753       PetscInt fStart[32], fEnd[32];
7754 
7755       fStart[0] = 0;
7756       fEnd[0]   = 0;
7757       for (PetscInt f = 0; f < numFields; f++) {
7758         PetscInt fDof = 0;
7759 
7760         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7761         fStart[f + 1] = fStart[f] + fDof;
7762         fEnd[f + 1]   = fStart[f + 1];
7763       }
7764       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7765       // only apply permutations on one side
7766       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7767       for (PetscInt f = 0; f < numFields; f++) {
7768         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7769       }
7770     } else {
7771       PetscInt bEnd = 0;
7772 
7773       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7774       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7775 
7776       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7777     }
7778   }
7779   PetscFunctionReturn(PETSC_SUCCESS);
7780 }
7781 
7782 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[])
7783 {
7784   Mat             cMat;
7785   PetscSection    aSec, cSec;
7786   IS              aIS;
7787   PetscInt        aStart = -1, aEnd = -1;
7788   PetscInt        sStart = -1, sEnd = -1;
7789   PetscInt        cStart = -1, cEnd = -1;
7790   const PetscInt *anchors;
7791   PetscInt        numFields, p;
7792   PetscInt        newNumPoints = 0, newNumIndices = 0;
7793   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7794   PetscInt        oldOffsets[32];
7795   PetscInt        newOffsets[32];
7796   PetscInt        oldOffsetsCopy[32];
7797   PetscInt        newOffsetsCopy[32];
7798   PetscScalar    *modMat         = NULL;
7799   PetscBool       anyConstrained = PETSC_FALSE;
7800 
7801   PetscFunctionBegin;
7802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7803   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7804   PetscCall(PetscSectionGetNumFields(section, &numFields));
7805 
7806   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7807   /* if there are point-to-point constraints */
7808   if (aSec) {
7809     PetscCall(PetscArrayzero(newOffsets, 32));
7810     PetscCall(PetscArrayzero(oldOffsets, 32));
7811     PetscCall(ISGetIndices(aIS, &anchors));
7812     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7813     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7814     /* figure out how many points are going to be in the new element matrix
7815      * (we allow double counting, because it's all just going to be summed
7816      * into the global matrix anyway) */
7817     for (p = 0; p < 2 * numPoints; p += 2) {
7818       PetscInt b    = points[p];
7819       PetscInt bDof = 0, bSecDof = 0;
7820 
7821       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7822       if (!bSecDof) continue;
7823 
7824       for (PetscInt f = 0; f < numFields; f++) {
7825         PetscInt fDof = 0;
7826 
7827         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7828         oldOffsets[f + 1] += fDof;
7829       }
7830       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7831       if (bDof) {
7832         /* this point is constrained */
7833         /* it is going to be replaced by its anchors */
7834         PetscInt bOff, q;
7835 
7836         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7837         for (q = 0; q < bDof; q++) {
7838           PetscInt a    = anchors[bOff + q];
7839           PetscInt aDof = 0;
7840 
7841           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7842           if (aDof) {
7843             anyConstrained = PETSC_TRUE;
7844             newNumPoints += 1;
7845           }
7846           newNumIndices += aDof;
7847           for (PetscInt f = 0; f < numFields; ++f) {
7848             PetscInt fDof = 0;
7849 
7850             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7851             newOffsets[f + 1] += fDof;
7852           }
7853         }
7854       } else {
7855         /* this point is not constrained */
7856         newNumPoints++;
7857         newNumIndices += bSecDof;
7858         for (PetscInt f = 0; f < numFields; ++f) {
7859           PetscInt fDof;
7860 
7861           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7862           newOffsets[f + 1] += fDof;
7863         }
7864       }
7865     }
7866   }
7867   if (!anyConstrained) {
7868     if (outNumPoints) *outNumPoints = 0;
7869     if (outNumIndices) *outNumIndices = 0;
7870     if (outPoints) *outPoints = NULL;
7871     if (outMat) *outMat = NULL;
7872     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7873     PetscFunctionReturn(PETSC_SUCCESS);
7874   }
7875 
7876   if (outNumPoints) *outNumPoints = newNumPoints;
7877   if (outNumIndices) *outNumIndices = newNumIndices;
7878 
7879   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7880   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7881 
7882   if (!outPoints && !outMat) {
7883     if (offsets) {
7884       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7885     }
7886     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7887     PetscFunctionReturn(PETSC_SUCCESS);
7888   }
7889 
7890   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7891   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7892 
7893   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7894   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7895 
7896   /* output arrays */
7897   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7898   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7899 
7900   // get the new Points
7901   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7902     PetscInt b    = points[2 * p];
7903     PetscInt bDof = 0, bSecDof = 0, bOff;
7904 
7905     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7906     if (!bSecDof) continue;
7907     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7908     if (bDof) {
7909       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7910       for (PetscInt q = 0; q < bDof; q++) {
7911         PetscInt a = anchors[bOff + q], aDof = 0;
7912 
7913         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7914         if (aDof) {
7915           newPoints[2 * newP]     = a;
7916           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7917           newP++;
7918         }
7919       }
7920     } else {
7921       newPoints[2 * newP]     = b;
7922       newPoints[2 * newP + 1] = points[2 * p + 1];
7923       newP++;
7924     }
7925   }
7926 
7927   if (outMat) {
7928     PetscScalar *tmpMat;
7929     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7930     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7931 
7932     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7933     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7934     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7935     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7936 
7937     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7938     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7939 
7940     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7941     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7942 
7943     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7944     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7945     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7946     // for each field, insert the anchor modification into modMat
7947     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7948       PetscInt fStart    = oldOffsets[f];
7949       PetscInt fNewStart = newOffsets[f];
7950       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7951         PetscInt b    = points[2 * p];
7952         PetscInt bDof = 0, bSecDof = 0, bOff;
7953 
7954         if (b >= sStart && b < sEnd) {
7955           if (numFields) {
7956             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7957           } else {
7958             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7959           }
7960         }
7961         if (!bSecDof) continue;
7962         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7963         if (bDof) {
7964           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7965           for (PetscInt q = 0; q < bDof; q++, newP++) {
7966             PetscInt a = anchors[bOff + q], aDof = 0;
7967 
7968             if (a >= sStart && a < sEnd) {
7969               if (numFields) {
7970                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7971               } else {
7972                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7973               }
7974             }
7975             if (aDof) {
7976               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7977               for (PetscInt d = 0; d < bSecDof; d++) {
7978                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7979               }
7980             }
7981             oNew += aDof;
7982           }
7983         } else {
7984           // Insert the identity matrix in this block
7985           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7986           oNew += bSecDof;
7987           newP++;
7988         }
7989         o += bSecDof;
7990       }
7991     }
7992 
7993     *outMat = modMat;
7994 
7995     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7996     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7997     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7998     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7999     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
8000   }
8001   PetscCall(ISRestoreIndices(aIS, &anchors));
8002 
8003   /* output */
8004   if (outPoints) {
8005     *outPoints = newPoints;
8006   } else {
8007     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
8008   }
8009   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
8010   PetscFunctionReturn(PETSC_SUCCESS);
8011 }
8012 
8013 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)
8014 {
8015   PetscScalar *modMat        = NULL;
8016   PetscInt     newNumIndices = -1;
8017 
8018   PetscFunctionBegin;
8019   /* 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.
8020      modMat is that matrix C */
8021   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
8022   if (outNumIndices) *outNumIndices = newNumIndices;
8023   if (modMat) {
8024     const PetscScalar *newValues = values;
8025 
8026     if (multiplyRight) {
8027       PetscScalar *newNewValues = NULL;
8028       PetscBLASInt M, N, K;
8029       PetscScalar  a = 1.0, b = 0.0;
8030 
8031       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);
8032 
8033       PetscCall(PetscBLASIntCast(newNumIndices, &M));
8034       PetscCall(PetscBLASIntCast(numRows, &N));
8035       PetscCall(PetscBLASIntCast(numIndices, &K));
8036       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
8037       // row-major to column-major conversion, right multiplication becomes left multiplication
8038       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
8039       numCols   = newNumIndices;
8040       newValues = newNewValues;
8041     }
8042 
8043     if (multiplyLeft) {
8044       PetscScalar *newNewValues = NULL;
8045       PetscBLASInt M, N, K;
8046       PetscScalar  a = 1.0, b = 0.0;
8047 
8048       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);
8049 
8050       PetscCall(PetscBLASIntCast(numCols, &M));
8051       PetscCall(PetscBLASIntCast(newNumIndices, &N));
8052       PetscCall(PetscBLASIntCast(numIndices, &K));
8053       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
8054       // row-major to column-major conversion, left multiplication becomes right multiplication
8055       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
8056       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
8057       newValues = newNewValues;
8058     }
8059     *outValues = (PetscScalar *)newValues;
8060     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
8061   }
8062   PetscFunctionReturn(PETSC_SUCCESS);
8063 }
8064 
8065 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)
8066 {
8067   PetscFunctionBegin;
8068   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8069   PetscFunctionReturn(PETSC_SUCCESS);
8070 }
8071 
8072 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8073 {
8074   /* Closure ordering */
8075   PetscSection    clSection;
8076   IS              clPoints;
8077   const PetscInt *clp;
8078   PetscInt       *points;
8079   PetscInt        Ncl, Ni = 0;
8080 
8081   PetscFunctionBeginHot;
8082   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8083   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8084     PetscInt dof;
8085 
8086     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8087     Ni += dof;
8088   }
8089   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8090   *closureSize = Ni;
8091   PetscFunctionReturn(PETSC_SUCCESS);
8092 }
8093 
8094 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)
8095 {
8096   /* Closure ordering */
8097   PetscSection    clSection;
8098   IS              clPoints;
8099   const PetscInt *clp;
8100   PetscInt       *points;
8101   const PetscInt *clperm = NULL;
8102   /* Dof permutation and sign flips */
8103   const PetscInt    **perms[32] = {NULL};
8104   const PetscScalar **flips[32] = {NULL};
8105   PetscScalar        *valCopy   = NULL;
8106   /* Hanging node constraints */
8107   PetscInt    *pointsC = NULL;
8108   PetscScalar *valuesC = NULL;
8109   PetscInt     NclC, NiC;
8110 
8111   PetscInt *idx;
8112   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8113   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8114   PetscInt  idxStart, idxEnd;
8115   PetscInt  nRows, nCols;
8116 
8117   PetscFunctionBeginHot;
8118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8119   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8120   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8121   PetscAssertPointer(numRows, 6);
8122   PetscAssertPointer(numCols, 7);
8123   if (indices) PetscAssertPointer(indices, 8);
8124   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8125   if (values) PetscAssertPointer(values, 10);
8126   PetscCall(PetscSectionGetNumFields(section, &Nf));
8127   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8128   PetscCall(PetscArrayzero(offsets, 32));
8129   /* 1) Get points in closure */
8130   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8131   if (useClPerm) {
8132     PetscInt depth, clsize;
8133     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8134     for (clsize = 0, p = 0; p < Ncl; p++) {
8135       PetscInt dof;
8136       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8137       clsize += dof;
8138     }
8139     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8140   }
8141   /* 2) Get number of indices on these points and field offsets from section */
8142   for (p = 0; p < Ncl * 2; p += 2) {
8143     PetscInt dof, fdof;
8144 
8145     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8146     for (f = 0; f < Nf; ++f) {
8147       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8148       offsets[f + 1] += fdof;
8149     }
8150     Ni += dof;
8151   }
8152   if (*numRows == -1) *numRows = Ni;
8153   if (*numCols == -1) *numCols = Ni;
8154   nRows = *numRows;
8155   nCols = *numCols;
8156   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8157   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8158   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8159   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8160   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8161   for (f = 0; f < PetscMax(1, Nf); ++f) {
8162     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8163     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8164     /* may need to apply sign changes to the element matrix */
8165     if (values && flips[f]) {
8166       PetscInt foffset = offsets[f];
8167 
8168       for (p = 0; p < Ncl; ++p) {
8169         PetscInt           pnt  = points[2 * p], fdof;
8170         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8171 
8172         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8173         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8174         if (flip) {
8175           PetscInt i, j, k;
8176 
8177           if (!valCopy) {
8178             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8179             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8180             *values = valCopy;
8181           }
8182           for (i = 0; i < fdof; ++i) {
8183             PetscScalar fval = flip[i];
8184 
8185             if (multiplyRight) {
8186               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8187             }
8188             if (multiplyLeft) {
8189               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8190             }
8191           }
8192         }
8193         foffset += fdof;
8194       }
8195     }
8196   }
8197   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8198   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8199   if (NclC) {
8200     if (multiplyRight) *numCols = NiC;
8201     if (multiplyLeft) *numRows = NiC;
8202     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8203     for (f = 0; f < PetscMax(1, Nf); ++f) {
8204       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8205       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8206     }
8207     for (f = 0; f < PetscMax(1, Nf); ++f) {
8208       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8209       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8210     }
8211     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8212     Ncl    = NclC;
8213     Ni     = NiC;
8214     points = pointsC;
8215     if (values) *values = valuesC;
8216   }
8217   /* 5) Calculate indices */
8218   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8219   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8220   if (Nf) {
8221     PetscInt  idxOff;
8222     PetscBool useFieldOffsets;
8223 
8224     if (outOffsets) {
8225       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8226     }
8227     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8228     if (useFieldOffsets) {
8229       for (p = 0; p < Ncl; ++p) {
8230         const PetscInt pnt = points[p * 2];
8231 
8232         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8233       }
8234     } else {
8235       for (p = 0; p < Ncl; ++p) {
8236         const PetscInt pnt = points[p * 2];
8237 
8238         if (pnt < idxStart || pnt >= idxEnd) continue;
8239         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8240         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8241          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8242          * global section. */
8243         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8244       }
8245     }
8246   } else {
8247     PetscInt off = 0, idxOff;
8248 
8249     for (p = 0; p < Ncl; ++p) {
8250       const PetscInt  pnt  = points[p * 2];
8251       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8252 
8253       if (pnt < idxStart || pnt >= idxEnd) continue;
8254       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8255       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8256        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8257       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8258     }
8259   }
8260   /* 6) Cleanup */
8261   for (f = 0; f < PetscMax(1, Nf); ++f) {
8262     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8263     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8264   }
8265   if (NclC) {
8266     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8267   } else {
8268     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8269   }
8270 
8271   if (indices) *indices = idx;
8272   PetscFunctionReturn(PETSC_SUCCESS);
8273 }
8274 
8275 /*@C
8276   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8277 
8278   Not collective
8279 
8280   Input Parameters:
8281 + dm         - The `DM`
8282 . section    - The `PetscSection` describing the points (a local section)
8283 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8284 . point      - The point defining the closure
8285 - useClPerm  - Use the closure point permutation if available
8286 
8287   Output Parameters:
8288 + numIndices - The number of dof indices in the closure of point with the input sections
8289 . indices    - The dof indices
8290 . outOffsets - Array to write the field offsets into, or `NULL`
8291 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8292 
8293   Level: advanced
8294 
8295   Notes:
8296   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8297 
8298   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8299   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8300   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8301   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8302   indices (with the above semantics) are implied.
8303 
8304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8305           `PetscSection`, `DMGetGlobalSection()`
8306 @*/
8307 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8308 {
8309   PetscInt numRows = -1, numCols = -1;
8310 
8311   PetscFunctionBeginHot;
8312   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8313   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8314   *numIndices = numRows;
8315   PetscFunctionReturn(PETSC_SUCCESS);
8316 }
8317 
8318 /*@C
8319   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8320 
8321   Not collective
8322 
8323   Input Parameters:
8324 + dm         - The `DM`
8325 . section    - The `PetscSection` describing the points (a local section)
8326 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8327 . point      - The point defining the closure
8328 - useClPerm  - Use the closure point permutation if available
8329 
8330   Output Parameters:
8331 + numIndices - The number of dof indices in the closure of point with the input sections
8332 . indices    - The dof indices
8333 . outOffsets - Array to write the field offsets into, or `NULL`
8334 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8335 
8336   Level: advanced
8337 
8338   Notes:
8339   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8340 
8341   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8342   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8343   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8344   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8345   indices (with the above semantics) are implied.
8346 
8347 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8348 @*/
8349 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8350 {
8351   PetscFunctionBegin;
8352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8353   PetscAssertPointer(indices, 7);
8354   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8355   PetscFunctionReturn(PETSC_SUCCESS);
8356 }
8357 
8358 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8359 {
8360   DM_Plex           *mesh = (DM_Plex *)dm->data;
8361   PetscInt          *indices;
8362   PetscInt           numIndices;
8363   const PetscScalar *valuesOrig = values;
8364   PetscErrorCode     ierr;
8365 
8366   PetscFunctionBegin;
8367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8368   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8369   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8370   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8371   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8372   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8373 
8374   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8375 
8376   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8377   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8378   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8379   if (ierr) {
8380     PetscMPIInt rank;
8381 
8382     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8383     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8384     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8385     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8386     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8387     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8388   }
8389   if (mesh->printFEM > 1) {
8390     PetscInt i;
8391     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8392     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8393     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8394   }
8395 
8396   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8397   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8398   PetscFunctionReturn(PETSC_SUCCESS);
8399 }
8400 
8401 /*@C
8402   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8403 
8404   Not collective
8405 
8406   Input Parameters:
8407 + dm            - The `DM`
8408 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8409 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8410 . A             - The matrix
8411 . point         - The point in the `DM`
8412 . values        - The array of values
8413 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8414 
8415   Level: intermediate
8416 
8417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8418 @*/
8419 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8420 {
8421   PetscFunctionBegin;
8422   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8423   PetscFunctionReturn(PETSC_SUCCESS);
8424 }
8425 
8426 /*@C
8427   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8428 
8429   Not collective
8430 
8431   Input Parameters:
8432 + dmRow            - The `DM` for the row fields
8433 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8434 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8435 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8436 . dmCol            - The `DM` for the column fields
8437 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8438 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8439 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8440 . A                - The matrix
8441 . point            - The point in the `DM`
8442 . values           - The array of values
8443 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8444 
8445   Level: intermediate
8446 
8447 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8448 @*/
8449 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)
8450 {
8451   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8452   PetscInt          *indicesRow, *indicesCol;
8453   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8454   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8455 
8456   PetscErrorCode ierr;
8457 
8458   PetscFunctionBegin;
8459   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8460   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8461   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8462   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8463   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8464   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8465   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8466   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8467   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8468   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8469   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8470 
8471   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8472   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8473   valuesV1 = valuesV0;
8474   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8475   valuesV2 = valuesV1;
8476   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8477 
8478   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8479   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8480   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8481   if (ierr) {
8482     PetscMPIInt rank;
8483 
8484     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8485     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8486     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8487     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8488     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8489     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8490     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8491   }
8492 
8493   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8494   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8495   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8496   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8497   PetscFunctionReturn(PETSC_SUCCESS);
8498 }
8499 
8500 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8501 {
8502   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8503   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8504   PetscInt       *cpoints = NULL;
8505   PetscInt       *findices, *cindices;
8506   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8507   PetscInt        foffsets[32], coffsets[32];
8508   DMPolytopeType  ct;
8509   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8510   PetscErrorCode  ierr;
8511 
8512   PetscFunctionBegin;
8513   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8514   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8515   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8516   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8517   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8518   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8519   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8520   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8521   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8522   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8523   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8524   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8525   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8526   PetscCall(PetscArrayzero(foffsets, 32));
8527   PetscCall(PetscArrayzero(coffsets, 32));
8528   /* Column indices */
8529   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8530   maxFPoints = numCPoints;
8531   /* Compress out points not in the section */
8532   /*   TODO: Squeeze out points with 0 dof as well */
8533   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8534   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8535     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8536       cpoints[q * 2]     = cpoints[p];
8537       cpoints[q * 2 + 1] = cpoints[p + 1];
8538       ++q;
8539     }
8540   }
8541   numCPoints = q;
8542   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8543     PetscInt fdof;
8544 
8545     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8546     if (!dof) continue;
8547     for (f = 0; f < numFields; ++f) {
8548       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8549       coffsets[f + 1] += fdof;
8550     }
8551     numCIndices += dof;
8552   }
8553   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8554   /* Row indices */
8555   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8556   {
8557     DMPlexTransform tr;
8558     DMPolytopeType *rct;
8559     PetscInt       *rsize, *rcone, *rornt, Nt;
8560 
8561     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8562     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8563     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8564     numSubcells = rsize[Nt - 1];
8565     PetscCall(DMPlexTransformDestroy(&tr));
8566   }
8567   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8568   for (r = 0, q = 0; r < numSubcells; ++r) {
8569     /* TODO Map from coarse to fine cells */
8570     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8571     /* Compress out points not in the section */
8572     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8573     for (p = 0; p < numFPoints * 2; p += 2) {
8574       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8575         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8576         if (!dof) continue;
8577         for (s = 0; s < q; ++s)
8578           if (fpoints[p] == ftotpoints[s * 2]) break;
8579         if (s < q) continue;
8580         ftotpoints[q * 2]     = fpoints[p];
8581         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8582         ++q;
8583       }
8584     }
8585     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8586   }
8587   numFPoints = q;
8588   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8589     PetscInt fdof;
8590 
8591     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8592     if (!dof) continue;
8593     for (f = 0; f < numFields; ++f) {
8594       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8595       foffsets[f + 1] += fdof;
8596     }
8597     numFIndices += dof;
8598   }
8599   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8600 
8601   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8602   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8603   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8604   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8605   if (numFields) {
8606     const PetscInt **permsF[32] = {NULL};
8607     const PetscInt **permsC[32] = {NULL};
8608 
8609     for (f = 0; f < numFields; f++) {
8610       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8611       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8612     }
8613     for (p = 0; p < numFPoints; p++) {
8614       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8615       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8616     }
8617     for (p = 0; p < numCPoints; p++) {
8618       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8619       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8620     }
8621     for (f = 0; f < numFields; f++) {
8622       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8623       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8624     }
8625   } else {
8626     const PetscInt **permsF = NULL;
8627     const PetscInt **permsC = NULL;
8628 
8629     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8630     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8631     for (p = 0, off = 0; p < numFPoints; p++) {
8632       const PetscInt *perm = permsF ? permsF[p] : NULL;
8633 
8634       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8635       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8636     }
8637     for (p = 0, off = 0; p < numCPoints; p++) {
8638       const PetscInt *perm = permsC ? permsC[p] : NULL;
8639 
8640       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8641       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8642     }
8643     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8644     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8645   }
8646   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8647   /* TODO: flips */
8648   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8649   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8650   if (ierr) {
8651     PetscMPIInt rank;
8652 
8653     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8654     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8655     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8656     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8657     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8658   }
8659   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8660   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8661   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8662   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8663   PetscFunctionReturn(PETSC_SUCCESS);
8664 }
8665 
8666 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8667 {
8668   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8669   PetscInt       *cpoints      = NULL;
8670   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8671   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8672   DMPolytopeType  ct;
8673   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8674 
8675   PetscFunctionBegin;
8676   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8677   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8678   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8679   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8680   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8681   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8682   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8683   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8684   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8685   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8686   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8687   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8688   /* Column indices */
8689   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8690   maxFPoints = numCPoints;
8691   /* Compress out points not in the section */
8692   /*   TODO: Squeeze out points with 0 dof as well */
8693   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8694   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8695     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8696       cpoints[q * 2]     = cpoints[p];
8697       cpoints[q * 2 + 1] = cpoints[p + 1];
8698       ++q;
8699     }
8700   }
8701   numCPoints = q;
8702   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8703     PetscInt fdof;
8704 
8705     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8706     if (!dof) continue;
8707     for (f = 0; f < numFields; ++f) {
8708       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8709       coffsets[f + 1] += fdof;
8710     }
8711     numCIndices += dof;
8712   }
8713   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8714   /* Row indices */
8715   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8716   {
8717     DMPlexTransform tr;
8718     DMPolytopeType *rct;
8719     PetscInt       *rsize, *rcone, *rornt, Nt;
8720 
8721     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8722     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8723     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8724     numSubcells = rsize[Nt - 1];
8725     PetscCall(DMPlexTransformDestroy(&tr));
8726   }
8727   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8728   for (r = 0, q = 0; r < numSubcells; ++r) {
8729     /* TODO Map from coarse to fine cells */
8730     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8731     /* Compress out points not in the section */
8732     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8733     for (p = 0; p < numFPoints * 2; p += 2) {
8734       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8735         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8736         if (!dof) continue;
8737         for (s = 0; s < q; ++s)
8738           if (fpoints[p] == ftotpoints[s * 2]) break;
8739         if (s < q) continue;
8740         ftotpoints[q * 2]     = fpoints[p];
8741         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8742         ++q;
8743       }
8744     }
8745     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8746   }
8747   numFPoints = q;
8748   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8749     PetscInt fdof;
8750 
8751     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8752     if (!dof) continue;
8753     for (f = 0; f < numFields; ++f) {
8754       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8755       foffsets[f + 1] += fdof;
8756     }
8757     numFIndices += dof;
8758   }
8759   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8760 
8761   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8762   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8763   if (numFields) {
8764     const PetscInt **permsF[32] = {NULL};
8765     const PetscInt **permsC[32] = {NULL};
8766 
8767     for (f = 0; f < numFields; f++) {
8768       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8769       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8770     }
8771     for (p = 0; p < numFPoints; p++) {
8772       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8773       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8774     }
8775     for (p = 0; p < numCPoints; p++) {
8776       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8777       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8778     }
8779     for (f = 0; f < numFields; f++) {
8780       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8781       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8782     }
8783   } else {
8784     const PetscInt **permsF = NULL;
8785     const PetscInt **permsC = NULL;
8786 
8787     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8788     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8789     for (p = 0, off = 0; p < numFPoints; p++) {
8790       const PetscInt *perm = permsF ? permsF[p] : NULL;
8791 
8792       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8793       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8794     }
8795     for (p = 0, off = 0; p < numCPoints; p++) {
8796       const PetscInt *perm = permsC ? permsC[p] : NULL;
8797 
8798       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8799       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8800     }
8801     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8802     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8803   }
8804   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8805   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8806   PetscFunctionReturn(PETSC_SUCCESS);
8807 }
8808 
8809 /*@
8810   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8811 
8812   Input Parameter:
8813 . dm - The `DMPLEX` object
8814 
8815   Output Parameter:
8816 . cellHeight - The height of a cell
8817 
8818   Level: developer
8819 
8820 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8821 @*/
8822 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8823 {
8824   DM_Plex *mesh = (DM_Plex *)dm->data;
8825 
8826   PetscFunctionBegin;
8827   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8828   PetscAssertPointer(cellHeight, 2);
8829   *cellHeight = mesh->vtkCellHeight;
8830   PetscFunctionReturn(PETSC_SUCCESS);
8831 }
8832 
8833 /*@
8834   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8835 
8836   Input Parameters:
8837 + dm         - The `DMPLEX` object
8838 - cellHeight - The height of a cell
8839 
8840   Level: developer
8841 
8842 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8843 @*/
8844 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8845 {
8846   DM_Plex *mesh = (DM_Plex *)dm->data;
8847 
8848   PetscFunctionBegin;
8849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8850   mesh->vtkCellHeight = cellHeight;
8851   PetscFunctionReturn(PETSC_SUCCESS);
8852 }
8853 
8854 /*@
8855   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8856 
8857   Input Parameters:
8858 + dm - The `DMPLEX` object
8859 - ct - The `DMPolytopeType` of the cell
8860 
8861   Output Parameters:
8862 + start - The first cell of this type, or `NULL`
8863 - end   - The upper bound on this celltype, or `NULL`
8864 
8865   Level: advanced
8866 
8867 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8868 @*/
8869 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8870 {
8871   DM_Plex *mesh = (DM_Plex *)dm->data;
8872   DMLabel  label;
8873   PetscInt pStart, pEnd;
8874 
8875   PetscFunctionBegin;
8876   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8877   if (start) {
8878     PetscAssertPointer(start, 3);
8879     *start = 0;
8880   }
8881   if (end) {
8882     PetscAssertPointer(end, 4);
8883     *end = 0;
8884   }
8885   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8886   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8887   if (mesh->tr) {
8888     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8889   } else {
8890     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8891     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8892     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8893   }
8894   PetscFunctionReturn(PETSC_SUCCESS);
8895 }
8896 
8897 /*@
8898   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8899 
8900   Input Parameters:
8901 + dm    - The `DMPLEX` object
8902 - depth - The depth for the given point stratum
8903 
8904   Output Parameter:
8905 . gsize - The global number of points in the stratum
8906 
8907   Level: advanced
8908 
8909 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8910 @*/
8911 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8912 {
8913   PetscSF         sf;
8914   const PetscInt *leaves;
8915   PetscInt        Nl, loc, start, end, lsize = 0;
8916 
8917   PetscFunctionBegin;
8918   PetscCall(DMGetPointSF(dm, &sf));
8919   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8920   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8921   for (PetscInt p = start; p < end; ++p) {
8922     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8923     if (loc < 0) ++lsize;
8924   }
8925   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8926   PetscFunctionReturn(PETSC_SUCCESS);
8927 }
8928 
8929 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8930 {
8931   PetscSection section, globalSection;
8932   PetscInt    *numbers, p;
8933 
8934   PetscFunctionBegin;
8935   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8936   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8937   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8938   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8939   PetscCall(PetscSectionSetUp(section));
8940   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8941   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8942   for (p = pStart; p < pEnd; ++p) {
8943     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8944     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8945     else numbers[p - pStart] += shift;
8946   }
8947   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8948   if (globalSize) {
8949     PetscLayout layout;
8950     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8951     PetscCall(PetscLayoutGetSize(layout, globalSize));
8952     PetscCall(PetscLayoutDestroy(&layout));
8953   }
8954   PetscCall(PetscSectionDestroy(&section));
8955   PetscCall(PetscSectionDestroy(&globalSection));
8956   PetscFunctionReturn(PETSC_SUCCESS);
8957 }
8958 
8959 /*@
8960   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8961 
8962   Input Parameters:
8963 + dm         - The `DMPLEX` object
8964 - includeAll - Whether to include all cells, or just the simplex and box cells
8965 
8966   Output Parameter:
8967 . globalCellNumbers - Global cell numbers for all cells on this process
8968 
8969   Level: developer
8970 
8971 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8972 @*/
8973 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8974 {
8975   PetscInt cellHeight, cStart, cEnd;
8976 
8977   PetscFunctionBegin;
8978   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8979   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8980   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8981   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8982   PetscFunctionReturn(PETSC_SUCCESS);
8983 }
8984 
8985 /*@
8986   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8987 
8988   Input Parameter:
8989 . dm - The `DMPLEX` object
8990 
8991   Output Parameter:
8992 . globalCellNumbers - Global cell numbers for all cells on this process
8993 
8994   Level: developer
8995 
8996 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8997 @*/
8998 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8999 {
9000   DM_Plex *mesh = (DM_Plex *)dm->data;
9001 
9002   PetscFunctionBegin;
9003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9004   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
9005   *globalCellNumbers = mesh->globalCellNumbers;
9006   PetscFunctionReturn(PETSC_SUCCESS);
9007 }
9008 
9009 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
9010 {
9011   PetscInt vStart, vEnd;
9012 
9013   PetscFunctionBegin;
9014   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9015   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9016   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
9017   PetscFunctionReturn(PETSC_SUCCESS);
9018 }
9019 
9020 /*@
9021   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
9022 
9023   Input Parameter:
9024 . dm - The `DMPLEX` object
9025 
9026   Output Parameter:
9027 . globalVertexNumbers - Global vertex numbers for all vertices on this process
9028 
9029   Level: developer
9030 
9031 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9032 @*/
9033 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
9034 {
9035   DM_Plex *mesh = (DM_Plex *)dm->data;
9036 
9037   PetscFunctionBegin;
9038   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9039   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
9040   *globalVertexNumbers = mesh->globalVertexNumbers;
9041   PetscFunctionReturn(PETSC_SUCCESS);
9042 }
9043 
9044 /*@
9045   DMPlexCreatePointNumbering - Create a global numbering for all points.
9046 
9047   Collective
9048 
9049   Input Parameter:
9050 . dm - The `DMPLEX` object
9051 
9052   Output Parameter:
9053 . globalPointNumbers - Global numbers for all points on this process
9054 
9055   Level: developer
9056 
9057   Notes:
9058   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
9059   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
9060   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
9061   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
9062 
9063   The partitioned mesh is
9064   ```
9065   (2)--0--(3)--1--(4)    (1)--0--(2)
9066   ```
9067   and its global numbering is
9068   ```
9069   (3)--0--(4)--1--(5)--2--(6)
9070   ```
9071   Then the global numbering is provided as
9072   ```
9073   [0] Number of indices in set 5
9074   [0] 0 0
9075   [0] 1 1
9076   [0] 2 3
9077   [0] 3 4
9078   [0] 4 -6
9079   [1] Number of indices in set 3
9080   [1] 0 2
9081   [1] 1 5
9082   [1] 2 6
9083   ```
9084 
9085 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9086 @*/
9087 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9088 {
9089   IS        nums[4];
9090   PetscInt  depths[4], gdepths[4], starts[4];
9091   PetscInt  depth, d, shift = 0;
9092   PetscBool empty = PETSC_FALSE;
9093 
9094   PetscFunctionBegin;
9095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9096   PetscCall(DMPlexGetDepth(dm, &depth));
9097   // For unstratified meshes use dim instead of depth
9098   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9099   // If any stratum is empty, we must mark all empty
9100   for (d = 0; d <= depth; ++d) {
9101     PetscInt end;
9102 
9103     depths[d] = depth - d;
9104     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9105     if (!(starts[d] - end)) empty = PETSC_TRUE;
9106   }
9107   if (empty)
9108     for (d = 0; d <= depth; ++d) {
9109       depths[d] = -1;
9110       starts[d] = -1;
9111     }
9112   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9113   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9114   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]);
9115   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9116   for (d = 0; d <= depth; ++d) {
9117     PetscInt pStart, pEnd, gsize;
9118 
9119     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9120     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9121     shift += gsize;
9122   }
9123   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9124   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9125   PetscFunctionReturn(PETSC_SUCCESS);
9126 }
9127 
9128 /*@
9129   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9130 
9131   Collective
9132 
9133   Input Parameter:
9134 . dm - The `DMPLEX` object
9135 
9136   Output Parameter:
9137 . globalEdgeNumbers - Global numbers for all edges on this process
9138 
9139   Level: developer
9140 
9141   Notes:
9142   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).
9143 
9144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9145 @*/
9146 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9147 {
9148   PetscSF  sf;
9149   PetscInt eStart, eEnd;
9150 
9151   PetscFunctionBegin;
9152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9153   PetscCall(DMGetPointSF(dm, &sf));
9154   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9155   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9156   PetscFunctionReturn(PETSC_SUCCESS);
9157 }
9158 
9159 /*@
9160   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9161 
9162   Input Parameter:
9163 . dm - The `DMPLEX` object
9164 
9165   Output Parameter:
9166 . ranks - The rank field
9167 
9168   Options Database Key:
9169 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9170 
9171   Level: intermediate
9172 
9173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9174 @*/
9175 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9176 {
9177   DM             rdm;
9178   PetscFE        fe;
9179   PetscScalar   *r;
9180   PetscMPIInt    rank;
9181   DMPolytopeType ct;
9182   PetscInt       dim, cStart, cEnd, c;
9183   PetscBool      simplex;
9184 
9185   PetscFunctionBeginUser;
9186   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9187   PetscAssertPointer(ranks, 2);
9188   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9189   PetscCall(DMClone(dm, &rdm));
9190   PetscCall(DMGetDimension(rdm, &dim));
9191   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9192   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9193   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9194   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9195   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9196   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9197   PetscCall(PetscFEDestroy(&fe));
9198   PetscCall(DMCreateDS(rdm));
9199   PetscCall(DMCreateGlobalVector(rdm, ranks));
9200   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9201   PetscCall(VecGetArray(*ranks, &r));
9202   for (c = cStart; c < cEnd; ++c) {
9203     PetscScalar *lr;
9204 
9205     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9206     if (lr) *lr = rank;
9207   }
9208   PetscCall(VecRestoreArray(*ranks, &r));
9209   PetscCall(DMDestroy(&rdm));
9210   PetscFunctionReturn(PETSC_SUCCESS);
9211 }
9212 
9213 /*@
9214   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9215 
9216   Input Parameters:
9217 + dm    - The `DMPLEX`
9218 - label - The `DMLabel`
9219 
9220   Output Parameter:
9221 . val - The label value field
9222 
9223   Options Database Key:
9224 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9225 
9226   Level: intermediate
9227 
9228 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9229 @*/
9230 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9231 {
9232   DM             rdm, plex;
9233   Vec            lval;
9234   PetscSection   section;
9235   PetscFE        fe;
9236   PetscScalar   *v;
9237   PetscInt       dim, pStart, pEnd, p, cStart;
9238   DMPolytopeType ct;
9239   char           name[PETSC_MAX_PATH_LEN];
9240   const char    *lname, *prefix;
9241 
9242   PetscFunctionBeginUser;
9243   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9244   PetscAssertPointer(label, 2);
9245   PetscAssertPointer(val, 3);
9246   PetscCall(DMClone(dm, &rdm));
9247   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9248   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9249   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9250   PetscCall(DMDestroy(&plex));
9251   PetscCall(DMGetDimension(rdm, &dim));
9252   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9253   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9254   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9255   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9256   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9257   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9258   PetscCall(PetscFEDestroy(&fe));
9259   PetscCall(DMCreateDS(rdm));
9260   PetscCall(DMCreateGlobalVector(rdm, val));
9261   PetscCall(DMCreateLocalVector(rdm, &lval));
9262   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9263   PetscCall(DMGetLocalSection(rdm, &section));
9264   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9265   PetscCall(VecGetArray(lval, &v));
9266   for (p = pStart; p < pEnd; ++p) {
9267     PetscInt cval, dof, off;
9268 
9269     PetscCall(PetscSectionGetDof(section, p, &dof));
9270     if (!dof) continue;
9271     PetscCall(DMLabelGetValue(label, p, &cval));
9272     PetscCall(PetscSectionGetOffset(section, p, &off));
9273     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9274   }
9275   PetscCall(VecRestoreArray(lval, &v));
9276   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9277   PetscCall(VecDestroy(&lval));
9278   PetscCall(DMDestroy(&rdm));
9279   PetscFunctionReturn(PETSC_SUCCESS);
9280 }
9281 
9282 /*@
9283   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9284 
9285   Input Parameter:
9286 . dm - The `DMPLEX` object
9287 
9288   Level: developer
9289 
9290   Notes:
9291   This is a useful diagnostic when creating meshes programmatically.
9292 
9293   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9294 
9295 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9296 @*/
9297 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9298 {
9299   PetscSection    coneSection, supportSection;
9300   const PetscInt *cone, *support;
9301   PetscInt        coneSize, c, supportSize, s;
9302   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9303   PetscBool       storagecheck = PETSC_TRUE;
9304 
9305   PetscFunctionBegin;
9306   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9307   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9308   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9309   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9310   /* Check that point p is found in the support of its cone points, and vice versa */
9311   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9312   for (p = pStart; p < pEnd; ++p) {
9313     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9314     PetscCall(DMPlexGetCone(dm, p, &cone));
9315     for (c = 0; c < coneSize; ++c) {
9316       PetscBool dup = PETSC_FALSE;
9317       PetscInt  d;
9318       for (d = c - 1; d >= 0; --d) {
9319         if (cone[c] == cone[d]) {
9320           dup = PETSC_TRUE;
9321           break;
9322         }
9323       }
9324       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9325       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9326       for (s = 0; s < supportSize; ++s) {
9327         if (support[s] == p) break;
9328       }
9329       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9330         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9331         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9332         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9333         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9334         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9335         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9336         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]);
9337         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9338       }
9339     }
9340     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9341     if (p != pp) {
9342       storagecheck = PETSC_FALSE;
9343       continue;
9344     }
9345     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9346     PetscCall(DMPlexGetSupport(dm, p, &support));
9347     for (s = 0; s < supportSize; ++s) {
9348       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9349       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9350       for (c = 0; c < coneSize; ++c) {
9351         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9352         if (cone[c] != pp) {
9353           c = 0;
9354           break;
9355         }
9356         if (cone[c] == p) break;
9357       }
9358       if (c >= coneSize) {
9359         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9360         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9361         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9362         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9363         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9364         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9365         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9366       }
9367     }
9368   }
9369   if (storagecheck) {
9370     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9371     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9372     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9373   }
9374   PetscFunctionReturn(PETSC_SUCCESS);
9375 }
9376 
9377 /*
9378   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.
9379 */
9380 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9381 {
9382   DMPolytopeType  cct;
9383   PetscInt        ptpoints[4];
9384   const PetscInt *cone, *ccone, *ptcone;
9385   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9386 
9387   PetscFunctionBegin;
9388   *unsplit = 0;
9389   switch (ct) {
9390   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9391     ptpoints[npt++] = c;
9392     break;
9393   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9394     PetscCall(DMPlexGetCone(dm, c, &cone));
9395     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9396     for (cp = 0; cp < coneSize; ++cp) {
9397       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9398       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9399     }
9400     break;
9401   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9402   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9403     PetscCall(DMPlexGetCone(dm, c, &cone));
9404     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9405     for (cp = 0; cp < coneSize; ++cp) {
9406       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9407       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9408       for (ccp = 0; ccp < cconeSize; ++ccp) {
9409         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9410         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9411           PetscInt p;
9412           for (p = 0; p < npt; ++p)
9413             if (ptpoints[p] == ccone[ccp]) break;
9414           if (p == npt) ptpoints[npt++] = ccone[ccp];
9415         }
9416       }
9417     }
9418     break;
9419   default:
9420     break;
9421   }
9422   for (pt = 0; pt < npt; ++pt) {
9423     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9424     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9425   }
9426   PetscFunctionReturn(PETSC_SUCCESS);
9427 }
9428 
9429 /*@
9430   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9431 
9432   Input Parameters:
9433 + dm         - The `DMPLEX` object
9434 - cellHeight - Normally 0
9435 
9436   Level: developer
9437 
9438   Notes:
9439   This is a useful diagnostic when creating meshes programmatically.
9440   Currently applicable only to homogeneous simplex or tensor meshes.
9441 
9442   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9443 
9444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9445 @*/
9446 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9447 {
9448   DMPlexInterpolatedFlag interp;
9449   DMPolytopeType         ct;
9450   PetscInt               vStart, vEnd, cStart, cEnd, c;
9451 
9452   PetscFunctionBegin;
9453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9454   PetscCall(DMPlexIsInterpolated(dm, &interp));
9455   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9456   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9457   for (c = cStart; c < cEnd; ++c) {
9458     PetscInt *closure = NULL;
9459     PetscInt  coneSize, closureSize, cl, Nv = 0;
9460 
9461     PetscCall(DMPlexGetCellType(dm, c, &ct));
9462     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9463     if (interp == DMPLEX_INTERPOLATED_FULL) {
9464       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9465       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));
9466     }
9467     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9468     for (cl = 0; cl < closureSize * 2; cl += 2) {
9469       const PetscInt p = closure[cl];
9470       if ((p >= vStart) && (p < vEnd)) ++Nv;
9471     }
9472     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9473     /* Special Case: Tensor faces with identified vertices */
9474     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9475       PetscInt unsplit;
9476 
9477       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9478       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9479     }
9480     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));
9481   }
9482   PetscFunctionReturn(PETSC_SUCCESS);
9483 }
9484 
9485 /*@
9486   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9487 
9488   Collective
9489 
9490   Input Parameters:
9491 + dm         - The `DMPLEX` object
9492 - cellHeight - Normally 0
9493 
9494   Level: developer
9495 
9496   Notes:
9497   This is a useful diagnostic when creating meshes programmatically.
9498   This routine is only relevant for meshes that are fully interpolated across all ranks.
9499   It will error out if a partially interpolated mesh is given on some rank.
9500   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9501 
9502   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9503 
9504 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9505 @*/
9506 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9507 {
9508   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9509   DMPlexInterpolatedFlag interpEnum;
9510 
9511   PetscFunctionBegin;
9512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9513   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9514   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9515   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9516     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9517     PetscFunctionReturn(PETSC_SUCCESS);
9518   }
9519 
9520   PetscCall(DMGetDimension(dm, &dim));
9521   PetscCall(DMPlexGetDepth(dm, &depth));
9522   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9523   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9524     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9525     for (c = cStart; c < cEnd; ++c) {
9526       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9527       const DMPolytopeType *faceTypes;
9528       DMPolytopeType        ct;
9529       PetscInt              numFaces, coneSize, f;
9530       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9531 
9532       PetscCall(DMPlexGetCellType(dm, c, &ct));
9533       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9534       if (unsplit) continue;
9535       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9536       PetscCall(DMPlexGetCone(dm, c, &cone));
9537       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9538       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9539       for (cl = 0; cl < closureSize * 2; cl += 2) {
9540         const PetscInt p = closure[cl];
9541         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9542       }
9543       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9544       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);
9545       for (f = 0; f < numFaces; ++f) {
9546         DMPolytopeType fct;
9547         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9548 
9549         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9550         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9551         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9552           const PetscInt p = fclosure[cl];
9553           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9554         }
9555         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]);
9556         for (v = 0; v < fnumCorners; ++v) {
9557           if (fclosure[v] != faces[fOff + v]) {
9558             PetscInt v1;
9559 
9560             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9561             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9562             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9563             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9564             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9565             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]);
9566           }
9567         }
9568         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9569         fOff += faceSizes[f];
9570       }
9571       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9572       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9573     }
9574   }
9575   PetscFunctionReturn(PETSC_SUCCESS);
9576 }
9577 
9578 /*@
9579   DMPlexCheckGeometry - Check the geometry of mesh cells
9580 
9581   Input Parameter:
9582 . dm - The `DMPLEX` object
9583 
9584   Level: developer
9585 
9586   Notes:
9587   This is a useful diagnostic when creating meshes programmatically.
9588 
9589   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9590 
9591 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9592 @*/
9593 PetscErrorCode DMPlexCheckGeometry(DM dm)
9594 {
9595   Vec       coordinates;
9596   PetscReal detJ, J[9], refVol = 1.0;
9597   PetscReal vol;
9598   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9599 
9600   PetscFunctionBegin;
9601   PetscCall(DMGetDimension(dm, &dim));
9602   PetscCall(DMGetCoordinateDim(dm, &dE));
9603   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9604   PetscCall(DMPlexGetDepth(dm, &depth));
9605   for (d = 0; d < dim; ++d) refVol *= 2.0;
9606   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9607   /* Make sure local coordinates are created, because that step is collective */
9608   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9609   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9610   for (c = cStart; c < cEnd; ++c) {
9611     DMPolytopeType ct;
9612     PetscInt       unsplit;
9613     PetscBool      ignoreZeroVol = PETSC_FALSE;
9614 
9615     PetscCall(DMPlexGetCellType(dm, c, &ct));
9616     switch (ct) {
9617     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9618     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9619     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9620       ignoreZeroVol = PETSC_TRUE;
9621       break;
9622     default:
9623       break;
9624     }
9625     switch (ct) {
9626     case DM_POLYTOPE_TRI_PRISM:
9627     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9628     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9629     case DM_POLYTOPE_PYRAMID:
9630       continue;
9631     default:
9632       break;
9633     }
9634     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9635     if (unsplit) continue;
9636     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9637     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);
9638     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9639     /* This should work with periodicity since DG coordinates should be used */
9640     if (depth > 1) {
9641       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9642       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);
9643       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9644     }
9645   }
9646   PetscFunctionReturn(PETSC_SUCCESS);
9647 }
9648 
9649 /*@
9650   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9651 
9652   Collective
9653 
9654   Input Parameters:
9655 + dm              - The `DMPLEX` object
9656 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9657 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9658 
9659   Level: developer
9660 
9661   Notes:
9662   This is mainly intended for debugging/testing purposes.
9663 
9664   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9665 
9666   Extra roots can come from periodic cuts, where additional points appear on the boundary
9667 
9668 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9669 @*/
9670 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9671 {
9672   PetscInt           l, nleaves, nroots, overlap;
9673   const PetscInt    *locals;
9674   const PetscSFNode *remotes;
9675   PetscBool          distributed;
9676   MPI_Comm           comm;
9677   PetscMPIInt        rank;
9678 
9679   PetscFunctionBegin;
9680   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9681   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9682   else pointSF = dm->sf;
9683   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9684   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9685   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9686   {
9687     PetscMPIInt mpiFlag;
9688 
9689     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9690     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9691   }
9692   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9693   PetscCall(DMPlexIsDistributed(dm, &distributed));
9694   if (!distributed) {
9695     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);
9696     PetscFunctionReturn(PETSC_SUCCESS);
9697   }
9698   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);
9699   PetscCall(DMPlexGetOverlap(dm, &overlap));
9700 
9701   /* Check SF graph is compatible with DMPlex chart */
9702   {
9703     PetscInt pStart, pEnd, maxLeaf;
9704 
9705     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9706     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9707     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9708     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9709   }
9710 
9711   /* Check there are no cells in interface */
9712   if (!overlap) {
9713     PetscInt cellHeight, cStart, cEnd;
9714 
9715     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9716     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9717     for (l = 0; l < nleaves; ++l) {
9718       const PetscInt point = locals ? locals[l] : l;
9719 
9720       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9721     }
9722   }
9723 
9724   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9725   {
9726     const PetscInt *rootdegree;
9727 
9728     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9729     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9730     for (l = 0; l < nleaves; ++l) {
9731       const PetscInt  point = locals ? locals[l] : l;
9732       const PetscInt *cone;
9733       PetscInt        coneSize, c, idx;
9734 
9735       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9736       PetscCall(DMPlexGetCone(dm, point, &cone));
9737       for (c = 0; c < coneSize; ++c) {
9738         if (!rootdegree[cone[c]]) {
9739           if (locals) {
9740             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9741           } else {
9742             idx = (cone[c] < nleaves) ? cone[c] : -1;
9743           }
9744           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9745         }
9746       }
9747     }
9748   }
9749   PetscFunctionReturn(PETSC_SUCCESS);
9750 }
9751 
9752 /*@
9753   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9754 
9755   Collective
9756 
9757   Input Parameter:
9758 . dm - The `DMPLEX` object
9759 
9760   Level: developer
9761 
9762   Notes:
9763   This is mainly intended for debugging/testing purposes.
9764 
9765   Other cell types which are disconnected would be caught by the symmetry and face checks.
9766 
9767   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9768 
9769 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9770 @*/
9771 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9772 {
9773   PetscInt pStart, pEnd, vStart, vEnd;
9774 
9775   PetscFunctionBegin;
9776   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9777   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9778   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9779   for (PetscInt v = vStart; v < vEnd; ++v) {
9780     PetscInt suppSize;
9781 
9782     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9783     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9784   }
9785   PetscFunctionReturn(PETSC_SUCCESS);
9786 }
9787 
9788 /*@
9789   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9790 
9791   Input Parameter:
9792 . dm - The `DMPLEX` object
9793 
9794   Level: developer
9795 
9796   Notes:
9797   This is a useful diagnostic when creating meshes programmatically.
9798 
9799   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9800 
9801   Currently does not include `DMPlexCheckCellShape()`.
9802 
9803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9804 @*/
9805 PetscErrorCode DMPlexCheck(DM dm)
9806 {
9807   PetscInt cellHeight;
9808 
9809   PetscFunctionBegin;
9810   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9811   PetscCall(DMPlexCheckSymmetry(dm));
9812   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9813   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9814   PetscCall(DMPlexCheckGeometry(dm));
9815   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9816   PetscCall(DMPlexCheckInterfaceCones(dm));
9817   PetscCall(DMPlexCheckOrphanVertices(dm));
9818   PetscFunctionReturn(PETSC_SUCCESS);
9819 }
9820 
9821 typedef struct cell_stats {
9822   PetscReal min, max, sum, squaresum;
9823   PetscInt  count;
9824 } cell_stats_t;
9825 
9826 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9827 {
9828   PetscInt i, N = *len;
9829 
9830   for (i = 0; i < N; i++) {
9831     cell_stats_t *A = (cell_stats_t *)a;
9832     cell_stats_t *B = (cell_stats_t *)b;
9833 
9834     B->min = PetscMin(A->min, B->min);
9835     B->max = PetscMax(A->max, B->max);
9836     B->sum += A->sum;
9837     B->squaresum += A->squaresum;
9838     B->count += A->count;
9839   }
9840 }
9841 
9842 /*@
9843   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9844 
9845   Collective
9846 
9847   Input Parameters:
9848 + dm        - The `DMPLEX` object
9849 . output    - If true, statistics will be displayed on `stdout`
9850 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9851 
9852   Level: developer
9853 
9854   Notes:
9855   This is mainly intended for debugging/testing purposes.
9856 
9857   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9858 
9859 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9860 @*/
9861 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9862 {
9863   DM           dmCoarse;
9864   cell_stats_t stats, globalStats;
9865   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9866   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9867   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9868   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9869   PetscMPIInt  rank, size;
9870 
9871   PetscFunctionBegin;
9872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9873   stats.min = PETSC_MAX_REAL;
9874   stats.max = PETSC_MIN_REAL;
9875   stats.sum = stats.squaresum = 0.;
9876   stats.count                 = 0;
9877 
9878   PetscCallMPI(MPI_Comm_size(comm, &size));
9879   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9880   PetscCall(DMGetCoordinateDim(dm, &cdim));
9881   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9882   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9883   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9884   for (c = cStart; c < cEnd; c++) {
9885     PetscInt  i;
9886     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9887 
9888     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9889     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9890     for (i = 0; i < PetscSqr(cdim); ++i) {
9891       frobJ += J[i] * J[i];
9892       frobInvJ += invJ[i] * invJ[i];
9893     }
9894     cond2 = frobJ * frobInvJ;
9895     cond  = PetscSqrtReal(cond2);
9896 
9897     stats.min = PetscMin(stats.min, cond);
9898     stats.max = PetscMax(stats.max, cond);
9899     stats.sum += cond;
9900     stats.squaresum += cond2;
9901     stats.count++;
9902     if (output && cond > limit) {
9903       PetscSection coordSection;
9904       Vec          coordsLocal;
9905       PetscScalar *coords = NULL;
9906       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9907 
9908       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9909       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9910       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9911       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9912       for (i = 0; i < Nv / cdim; ++i) {
9913         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9914         for (d = 0; d < cdim; ++d) {
9915           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9916           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9917         }
9918         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9919       }
9920       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9921       for (cl = 0; cl < clSize * 2; cl += 2) {
9922         const PetscInt edge = closure[cl];
9923 
9924         if ((edge >= eStart) && (edge < eEnd)) {
9925           PetscReal len;
9926 
9927           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9928           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9929         }
9930       }
9931       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9932       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9933     }
9934   }
9935   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9936 
9937   if (size > 1) {
9938     PetscMPIInt  blockLengths[2] = {4, 1};
9939     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9940     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9941     MPI_Op       statReduce;
9942 
9943     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9944     PetscCallMPI(MPI_Type_commit(&statType));
9945     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9946     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9947     PetscCallMPI(MPI_Op_free(&statReduce));
9948     PetscCallMPI(MPI_Type_free(&statType));
9949   } else {
9950     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9951   }
9952   if (rank == 0) {
9953     count = globalStats.count;
9954     min   = globalStats.min;
9955     max   = globalStats.max;
9956     mean  = globalStats.sum / globalStats.count;
9957     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9958   }
9959 
9960   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));
9961   PetscCall(PetscFree2(J, invJ));
9962 
9963   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9964   if (dmCoarse) {
9965     PetscBool isplex;
9966 
9967     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9968     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9969   }
9970   PetscFunctionReturn(PETSC_SUCCESS);
9971 }
9972 
9973 /*@
9974   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9975   orthogonal quality below given tolerance.
9976 
9977   Collective
9978 
9979   Input Parameters:
9980 + dm   - The `DMPLEX` object
9981 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9982 - atol - [0, 1] Absolute tolerance for tagging cells.
9983 
9984   Output Parameters:
9985 + OrthQual      - `Vec` containing orthogonal quality per cell
9986 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9987 
9988   Options Database Keys:
9989 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9990 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9991 
9992   Level: intermediate
9993 
9994   Notes:
9995   Orthogonal quality is given by the following formula\:
9996 
9997   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9998 
9999   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
10000   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
10001   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
10002   calculating the cosine of the angle between these vectors.
10003 
10004   Orthogonal quality ranges from 1 (best) to 0 (worst).
10005 
10006   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
10007   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
10008 
10009   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
10010 
10011 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
10012 @*/
10013 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
10014 {
10015   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
10016   PetscInt              *idx;
10017   PetscScalar           *oqVals;
10018   const PetscScalar     *cellGeomArr, *faceGeomArr;
10019   PetscReal             *ci, *fi, *Ai;
10020   MPI_Comm               comm;
10021   Vec                    cellgeom, facegeom;
10022   DM                     dmFace, dmCell;
10023   IS                     glob;
10024   ISLocalToGlobalMapping ltog;
10025   PetscViewer            vwr;
10026 
10027   PetscFunctionBegin;
10028   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10029   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
10030   PetscAssertPointer(OrthQual, 4);
10031   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
10032   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
10033   PetscCall(DMGetDimension(dm, &nc));
10034   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
10035   {
10036     DMPlexInterpolatedFlag interpFlag;
10037 
10038     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
10039     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
10040       PetscMPIInt rank;
10041 
10042       PetscCallMPI(MPI_Comm_rank(comm, &rank));
10043       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
10044     }
10045   }
10046   if (OrthQualLabel) {
10047     PetscAssertPointer(OrthQualLabel, 5);
10048     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
10049     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
10050   } else {
10051     *OrthQualLabel = NULL;
10052   }
10053   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
10054   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
10055   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
10056   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
10057   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
10058   PetscCall(VecCreate(comm, OrthQual));
10059   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
10060   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
10061   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
10062   PetscCall(VecSetUp(*OrthQual));
10063   PetscCall(ISDestroy(&glob));
10064   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
10065   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
10066   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
10067   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10068   PetscCall(VecGetDM(cellgeom, &dmCell));
10069   PetscCall(VecGetDM(facegeom, &dmFace));
10070   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10071   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10072     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10073     PetscInt         cellarr[2], *adj = NULL;
10074     PetscScalar     *cArr, *fArr;
10075     PetscReal        minvalc = 1.0, minvalf = 1.0;
10076     PetscFVCellGeom *cg;
10077 
10078     idx[cellIter] = cell - cStart;
10079     cellarr[0]    = cell;
10080     /* Make indexing into cellGeom easier */
10081     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10082     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10083     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10084     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10085     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10086       PetscInt         i;
10087       const PetscInt   neigh  = adj[cellneigh];
10088       PetscReal        normci = 0, normfi = 0, normai = 0;
10089       PetscFVCellGeom *cgneigh;
10090       PetscFVFaceGeom *fg;
10091 
10092       /* Don't count ourselves in the neighbor list */
10093       if (neigh == cell) continue;
10094       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10095       cellarr[1] = neigh;
10096       {
10097         PetscInt        numcovpts;
10098         const PetscInt *covpts;
10099 
10100         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10101         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10102         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10103       }
10104 
10105       /* Compute c_i, f_i and their norms */
10106       for (i = 0; i < nc; i++) {
10107         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10108         fi[i] = fg->centroid[i] - cg->centroid[i];
10109         Ai[i] = fg->normal[i];
10110         normci += PetscPowReal(ci[i], 2);
10111         normfi += PetscPowReal(fi[i], 2);
10112         normai += PetscPowReal(Ai[i], 2);
10113       }
10114       normci = PetscSqrtReal(normci);
10115       normfi = PetscSqrtReal(normfi);
10116       normai = PetscSqrtReal(normai);
10117 
10118       /* Normalize and compute for each face-cell-normal pair */
10119       for (i = 0; i < nc; i++) {
10120         ci[i] = ci[i] / normci;
10121         fi[i] = fi[i] / normfi;
10122         Ai[i] = Ai[i] / normai;
10123         /* PetscAbs because I don't know if normals are guaranteed to point out */
10124         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10125         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10126       }
10127       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10128       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10129     }
10130     PetscCall(PetscFree(adj));
10131     PetscCall(PetscFree2(cArr, fArr));
10132     /* Defer to cell if they're equal */
10133     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10134     if (OrthQualLabel) {
10135       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10136     }
10137   }
10138   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10139   PetscCall(VecAssemblyBegin(*OrthQual));
10140   PetscCall(VecAssemblyEnd(*OrthQual));
10141   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10142   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10143   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10144   if (OrthQualLabel) {
10145     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10146   }
10147   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10148   PetscCall(PetscViewerDestroy(&vwr));
10149   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10150   PetscFunctionReturn(PETSC_SUCCESS);
10151 }
10152 
10153 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10154  * interpolator construction */
10155 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10156 {
10157   PetscSection section, newSection, gsection;
10158   PetscSF      sf;
10159   PetscBool    hasConstraints, ghasConstraints;
10160 
10161   PetscFunctionBegin;
10162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10163   PetscAssertPointer(odm, 2);
10164   PetscCall(DMGetLocalSection(dm, &section));
10165   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10166   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10167   if (!ghasConstraints) {
10168     PetscCall(PetscObjectReference((PetscObject)dm));
10169     *odm = dm;
10170     PetscFunctionReturn(PETSC_SUCCESS);
10171   }
10172   PetscCall(DMClone(dm, odm));
10173   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10174   PetscCall(DMGetLocalSection(*odm, &newSection));
10175   PetscCall(DMGetPointSF(*odm, &sf));
10176   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10177   PetscCall(DMSetGlobalSection(*odm, gsection));
10178   PetscCall(PetscSectionDestroy(&gsection));
10179   PetscFunctionReturn(PETSC_SUCCESS);
10180 }
10181 
10182 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10183 {
10184   DM        dmco, dmfo;
10185   Mat       interpo;
10186   Vec       rscale;
10187   Vec       cglobalo, clocal;
10188   Vec       fglobal, fglobalo, flocal;
10189   PetscBool regular;
10190 
10191   PetscFunctionBegin;
10192   PetscCall(DMGetFullDM(dmc, &dmco));
10193   PetscCall(DMGetFullDM(dmf, &dmfo));
10194   PetscCall(DMSetCoarseDM(dmfo, dmco));
10195   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10196   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10197   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10198   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10199   PetscCall(DMCreateLocalVector(dmc, &clocal));
10200   PetscCall(VecSet(cglobalo, 0.));
10201   PetscCall(VecSet(clocal, 0.));
10202   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10203   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10204   PetscCall(DMCreateLocalVector(dmf, &flocal));
10205   PetscCall(VecSet(fglobal, 0.));
10206   PetscCall(VecSet(fglobalo, 0.));
10207   PetscCall(VecSet(flocal, 0.));
10208   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10209   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10210   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10211   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10212   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10213   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10214   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10215   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10216   *shift = fglobal;
10217   PetscCall(VecDestroy(&flocal));
10218   PetscCall(VecDestroy(&fglobalo));
10219   PetscCall(VecDestroy(&clocal));
10220   PetscCall(VecDestroy(&cglobalo));
10221   PetscCall(VecDestroy(&rscale));
10222   PetscCall(MatDestroy(&interpo));
10223   PetscCall(DMDestroy(&dmfo));
10224   PetscCall(DMDestroy(&dmco));
10225   PetscFunctionReturn(PETSC_SUCCESS);
10226 }
10227 
10228 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10229 {
10230   PetscObject shifto;
10231   Vec         shift;
10232 
10233   PetscFunctionBegin;
10234   if (!interp) {
10235     Vec rscale;
10236 
10237     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10238     PetscCall(VecDestroy(&rscale));
10239   } else {
10240     PetscCall(PetscObjectReference((PetscObject)interp));
10241   }
10242   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10243   if (!shifto) {
10244     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10245     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10246     shifto = (PetscObject)shift;
10247     PetscCall(VecDestroy(&shift));
10248   }
10249   shift = (Vec)shifto;
10250   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10251   PetscCall(VecAXPY(fineSol, 1.0, shift));
10252   PetscCall(MatDestroy(&interp));
10253   PetscFunctionReturn(PETSC_SUCCESS);
10254 }
10255 
10256 /* Pointwise interpolation
10257      Just code FEM for now
10258      u^f = I u^c
10259      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10260      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10261      I_{ij} = psi^f_i phi^c_j
10262 */
10263 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10264 {
10265   PetscSection gsc, gsf;
10266   PetscInt     m, n;
10267   void        *ctx;
10268   DM           cdm;
10269   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10270 
10271   PetscFunctionBegin;
10272   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10273   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10274   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10275   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10276 
10277   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10278   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10279   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10280   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10281   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10282 
10283   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10284   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10285   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10286   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10287   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10288   if (scaling) {
10289     /* Use naive scaling */
10290     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10291   }
10292   PetscFunctionReturn(PETSC_SUCCESS);
10293 }
10294 
10295 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10296 {
10297   VecScatter ctx;
10298 
10299   PetscFunctionBegin;
10300   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10301   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10302   PetscCall(VecScatterDestroy(&ctx));
10303   PetscFunctionReturn(PETSC_SUCCESS);
10304 }
10305 
10306 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[])
10307 {
10308   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10309   const PetscInt Nc = uOff[f + 1] - uOff[f];
10310   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10311 }
10312 
10313 // The assumption here is that the test field is a vector and the basis field is a scalar (so we need the gradient)
10314 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[])
10315 {
10316   for (PetscInt c = 0; c < dim; ++c) g1[c * dim + c] = 1.0;
10317 }
10318 
10319 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10320 {
10321   DM           dmc;
10322   PetscDS      ds;
10323   Vec          ones, locmass;
10324   IS           cellIS;
10325   PetscFormKey key;
10326   PetscInt     depth;
10327 
10328   PetscFunctionBegin;
10329   PetscCall(DMClone(dm, &dmc));
10330   PetscCall(DMCopyDisc(dm, dmc));
10331   PetscCall(DMGetDS(dmc, &ds));
10332   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10333   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10334   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10335   else PetscCall(DMGetLocalVector(dm, &locmass));
10336   PetscCall(DMGetLocalVector(dm, &ones));
10337   PetscCall(DMPlexGetDepth(dm, &depth));
10338   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10339   PetscCall(VecSet(locmass, 0.0));
10340   PetscCall(VecSet(ones, 1.0));
10341   key.label = NULL;
10342   key.value = 0;
10343   key.field = 0;
10344   key.part  = 0;
10345   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10346   PetscCall(ISDestroy(&cellIS));
10347   if (mass) {
10348     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10349     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10350   }
10351   PetscCall(DMRestoreLocalVector(dm, &ones));
10352   if (lmass) *lmass = locmass;
10353   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10354   PetscCall(DMDestroy(&dmc));
10355   PetscFunctionReturn(PETSC_SUCCESS);
10356 }
10357 
10358 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10359 {
10360   PetscSection gsc, gsf;
10361   PetscInt     m, n;
10362   void        *ctx;
10363   DM           cdm;
10364   PetscBool    regular;
10365 
10366   PetscFunctionBegin;
10367   if (dmFine == dmCoarse) {
10368     DM            dmc;
10369     PetscDS       ds;
10370     PetscWeakForm wf;
10371     Vec           u;
10372     IS            cellIS;
10373     PetscFormKey  key;
10374     PetscInt      depth;
10375 
10376     PetscCall(DMClone(dmFine, &dmc));
10377     PetscCall(DMCopyDisc(dmFine, dmc));
10378     PetscCall(DMGetDS(dmc, &ds));
10379     PetscCall(PetscDSGetWeakForm(ds, &wf));
10380     PetscCall(PetscWeakFormClear(wf));
10381     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10382     PetscCall(DMCreateMatrix(dmc, mass));
10383     PetscCall(DMGetLocalVector(dmc, &u));
10384     PetscCall(DMPlexGetDepth(dmc, &depth));
10385     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10386     PetscCall(MatZeroEntries(*mass));
10387     key.label = NULL;
10388     key.value = 0;
10389     key.field = 0;
10390     key.part  = 0;
10391     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10392     PetscCall(ISDestroy(&cellIS));
10393     PetscCall(DMRestoreLocalVector(dmc, &u));
10394     PetscCall(DMDestroy(&dmc));
10395   } else {
10396     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10397     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10398     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10399     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10400 
10401     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10402     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10403     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10404     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10405 
10406     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10407     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10408     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10409     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10410   }
10411   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10412   PetscFunctionReturn(PETSC_SUCCESS);
10413 }
10414 
10415 PetscErrorCode DMCreateGradientMatrix_Plex(DM dmc, DM dmr, Mat *derv)
10416 {
10417   PetscSection gsc, gsf;
10418   PetscInt     m, n;
10419   void        *ctx;
10420 
10421   PetscFunctionBegin;
10422   PetscCall(DMGetGlobalSection(dmr, &gsf));
10423   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10424   PetscCall(DMGetGlobalSection(dmc, &gsc));
10425   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10426 
10427   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmc), derv));
10428   PetscCall(PetscObjectSetName((PetscObject)*derv, "Plex Derivative Matrix"));
10429   PetscCall(MatSetSizes(*derv, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10430   PetscCall(MatSetType(*derv, dmc->mattype));
10431 
10432   PetscCall(DMGetApplicationContext(dmr, &ctx));
10433   {
10434     DM            ndmr;
10435     PetscDS       ds;
10436     PetscWeakForm wf;
10437     Vec           u;
10438     IS            cellIS;
10439     PetscFormKey  key;
10440     PetscInt      depth, Nf;
10441 
10442     PetscCall(DMClone(dmr, &ndmr));
10443     PetscCall(DMCopyDisc(dmr, ndmr));
10444     PetscCall(DMGetDS(ndmr, &ds));
10445     PetscCall(PetscDSGetWeakForm(ds, &wf));
10446     PetscCall(PetscWeakFormClear(wf));
10447     PetscCall(PetscDSGetNumFields(ds, &Nf));
10448     for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, NULL, g1_identity_private, NULL, NULL));
10449     PetscCall(DMGetLocalVector(ndmr, &u));
10450     PetscCall(DMPlexGetDepth(ndmr, &depth));
10451     PetscCall(DMGetStratumIS(ndmr, "depth", depth, &cellIS));
10452     PetscCall(MatZeroEntries(*derv));
10453     key.label = NULL;
10454     key.value = 0;
10455     key.field = 0;
10456     key.part  = 0;
10457     PetscCall(DMPlexComputeJacobianByKeyGeneral(ndmr, dmc, key, cellIS, 0.0, 0.0, u, NULL, *derv, *derv, NULL));
10458     PetscCall(ISDestroy(&cellIS));
10459     PetscCall(DMRestoreLocalVector(ndmr, &u));
10460     PetscCall(DMDestroy(&ndmr));
10461   }
10462   PetscCall(MatViewFromOptions(*derv, NULL, "-gradient_mat_view"));
10463   PetscFunctionReturn(PETSC_SUCCESS);
10464 }
10465 
10466 /*@
10467   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10468 
10469   Input Parameter:
10470 . dm - The `DMPLEX` object
10471 
10472   Output Parameter:
10473 . regular - The flag
10474 
10475   Level: intermediate
10476 
10477 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10478 @*/
10479 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10480 {
10481   PetscFunctionBegin;
10482   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10483   PetscAssertPointer(regular, 2);
10484   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10485   PetscFunctionReturn(PETSC_SUCCESS);
10486 }
10487 
10488 /*@
10489   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10490 
10491   Input Parameters:
10492 + dm      - The `DMPLEX` object
10493 - regular - The flag
10494 
10495   Level: intermediate
10496 
10497 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10498 @*/
10499 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10500 {
10501   PetscFunctionBegin;
10502   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10503   ((DM_Plex *)dm->data)->regularRefinement = regular;
10504   PetscFunctionReturn(PETSC_SUCCESS);
10505 }
10506 
10507 /*@
10508   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10509   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10510 
10511   Not Collective
10512 
10513   Input Parameter:
10514 . dm - The `DMPLEX` object
10515 
10516   Output Parameters:
10517 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10518 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10519 
10520   Level: intermediate
10521 
10522 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10523 @*/
10524 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10525 {
10526   DM_Plex *plex = (DM_Plex *)dm->data;
10527 
10528   PetscFunctionBegin;
10529   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10530   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10531   if (anchorSection) *anchorSection = plex->anchorSection;
10532   if (anchorIS) *anchorIS = plex->anchorIS;
10533   PetscFunctionReturn(PETSC_SUCCESS);
10534 }
10535 
10536 /*@
10537   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10538 
10539   Collective
10540 
10541   Input Parameters:
10542 + dm            - The `DMPLEX` object
10543 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10544                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10545 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10546 
10547   Level: intermediate
10548 
10549   Notes:
10550   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10551   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10552   combination of other points' degrees of freedom.
10553 
10554   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10555   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10556 
10557   The reference counts of `anchorSection` and `anchorIS` are incremented.
10558 
10559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10560 @*/
10561 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10562 {
10563   DM_Plex    *plex = (DM_Plex *)dm->data;
10564   PetscMPIInt result;
10565 
10566   PetscFunctionBegin;
10567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10568   if (anchorSection) {
10569     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10570     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10571     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10572   }
10573   if (anchorIS) {
10574     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10575     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10576     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10577   }
10578 
10579   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10580   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10581   plex->anchorSection = anchorSection;
10582 
10583   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10584   PetscCall(ISDestroy(&plex->anchorIS));
10585   plex->anchorIS = anchorIS;
10586 
10587   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10588     PetscInt        size, a, pStart, pEnd;
10589     const PetscInt *anchors;
10590 
10591     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10592     PetscCall(ISGetLocalSize(anchorIS, &size));
10593     PetscCall(ISGetIndices(anchorIS, &anchors));
10594     for (a = 0; a < size; a++) {
10595       PetscInt p;
10596 
10597       p = anchors[a];
10598       if (p >= pStart && p < pEnd) {
10599         PetscInt dof;
10600 
10601         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10602         if (dof) {
10603           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10604           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10605         }
10606       }
10607     }
10608     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10609   }
10610   /* reset the generic constraints */
10611   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10612   PetscFunctionReturn(PETSC_SUCCESS);
10613 }
10614 
10615 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10616 {
10617   PetscSection anchorSection;
10618   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10619 
10620   PetscFunctionBegin;
10621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10622   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10623   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10624   PetscCall(PetscSectionGetNumFields(section, &numFields));
10625   if (numFields) {
10626     PetscInt f;
10627     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10628 
10629     for (f = 0; f < numFields; f++) {
10630       PetscInt numComp;
10631 
10632       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10633       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10634     }
10635   }
10636   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10637   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10638   pStart = PetscMax(pStart, sStart);
10639   pEnd   = PetscMin(pEnd, sEnd);
10640   pEnd   = PetscMax(pStart, pEnd);
10641   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10642   for (p = pStart; p < pEnd; p++) {
10643     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10644     if (dof) {
10645       PetscCall(PetscSectionGetDof(section, p, &dof));
10646       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10647       for (f = 0; f < numFields; f++) {
10648         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10649         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10650       }
10651     }
10652   }
10653   PetscCall(PetscSectionSetUp(*cSec));
10654   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10655   PetscFunctionReturn(PETSC_SUCCESS);
10656 }
10657 
10658 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10659 {
10660   PetscSection    aSec;
10661   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10662   const PetscInt *anchors;
10663   PetscInt        numFields, f;
10664   IS              aIS;
10665   MatType         mtype;
10666   PetscBool       iscuda, iskokkos;
10667 
10668   PetscFunctionBegin;
10669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10670   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10671   PetscCall(PetscSectionGetStorageSize(section, &n));
10672   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10673   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10674   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10675   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10676   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10677   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10678   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10679   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10680   else mtype = MATSEQAIJ;
10681   PetscCall(MatSetType(*cMat, mtype));
10682   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10683   PetscCall(ISGetIndices(aIS, &anchors));
10684   /* cSec will be a subset of aSec and section */
10685   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10686   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10687   PetscCall(PetscMalloc1(m + 1, &i));
10688   i[0] = 0;
10689   PetscCall(PetscSectionGetNumFields(section, &numFields));
10690   for (p = pStart; p < pEnd; p++) {
10691     PetscInt rDof, rOff, r;
10692 
10693     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10694     if (!rDof) continue;
10695     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10696     if (numFields) {
10697       for (f = 0; f < numFields; f++) {
10698         annz = 0;
10699         for (r = 0; r < rDof; r++) {
10700           a = anchors[rOff + r];
10701           if (a < sStart || a >= sEnd) continue;
10702           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10703           annz += aDof;
10704         }
10705         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10706         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10707         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10708       }
10709     } else {
10710       annz = 0;
10711       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10712       for (q = 0; q < dof; q++) {
10713         a = anchors[rOff + q];
10714         if (a < sStart || a >= sEnd) continue;
10715         PetscCall(PetscSectionGetDof(section, a, &aDof));
10716         annz += aDof;
10717       }
10718       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10719       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10720       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10721     }
10722   }
10723   nnz = i[m];
10724   PetscCall(PetscMalloc1(nnz, &j));
10725   offset = 0;
10726   for (p = pStart; p < pEnd; p++) {
10727     if (numFields) {
10728       for (f = 0; f < numFields; f++) {
10729         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10730         for (q = 0; q < dof; q++) {
10731           PetscInt rDof, rOff, r;
10732           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10733           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10734           for (r = 0; r < rDof; r++) {
10735             PetscInt s;
10736 
10737             a = anchors[rOff + r];
10738             if (a < sStart || a >= sEnd) continue;
10739             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10740             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10741             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10742           }
10743         }
10744       }
10745     } else {
10746       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10747       for (q = 0; q < dof; q++) {
10748         PetscInt rDof, rOff, r;
10749         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10750         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10751         for (r = 0; r < rDof; r++) {
10752           PetscInt s;
10753 
10754           a = anchors[rOff + r];
10755           if (a < sStart || a >= sEnd) continue;
10756           PetscCall(PetscSectionGetDof(section, a, &aDof));
10757           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10758           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10759         }
10760       }
10761     }
10762   }
10763   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10764   PetscCall(PetscFree(i));
10765   PetscCall(PetscFree(j));
10766   PetscCall(ISRestoreIndices(aIS, &anchors));
10767   PetscFunctionReturn(PETSC_SUCCESS);
10768 }
10769 
10770 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10771 {
10772   DM_Plex     *plex = (DM_Plex *)dm->data;
10773   PetscSection anchorSection, section, cSec;
10774   Mat          cMat;
10775 
10776   PetscFunctionBegin;
10777   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10778   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10779   if (anchorSection) {
10780     PetscInt Nf;
10781 
10782     PetscCall(DMGetLocalSection(dm, &section));
10783     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10784     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10785     PetscCall(DMGetNumFields(dm, &Nf));
10786     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10787     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10788     PetscCall(PetscSectionDestroy(&cSec));
10789     PetscCall(MatDestroy(&cMat));
10790   }
10791   PetscFunctionReturn(PETSC_SUCCESS);
10792 }
10793 
10794 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10795 {
10796   IS           subis;
10797   PetscSection section, subsection;
10798 
10799   PetscFunctionBegin;
10800   PetscCall(DMGetLocalSection(dm, &section));
10801   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10802   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10803   /* Create subdomain */
10804   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10805   /* Create submodel */
10806   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10807   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10808   PetscCall(DMSetLocalSection(*subdm, subsection));
10809   PetscCall(PetscSectionDestroy(&subsection));
10810   PetscCall(DMCopyDisc(dm, *subdm));
10811   /* Create map from submodel to global model */
10812   if (is) {
10813     PetscSection    sectionGlobal, subsectionGlobal;
10814     IS              spIS;
10815     const PetscInt *spmap;
10816     PetscInt       *subIndices;
10817     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10818     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10819 
10820     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10821     PetscCall(ISGetIndices(spIS, &spmap));
10822     PetscCall(PetscSectionGetNumFields(section, &Nf));
10823     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10824     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10825     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10826     for (p = pStart; p < pEnd; ++p) {
10827       PetscInt gdof, pSubSize = 0;
10828 
10829       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10830       if (gdof > 0) {
10831         for (f = 0; f < Nf; ++f) {
10832           PetscInt fdof, fcdof;
10833 
10834           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10835           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10836           pSubSize += fdof - fcdof;
10837         }
10838         subSize += pSubSize;
10839         if (pSubSize) {
10840           if (bs < 0) {
10841             bs = pSubSize;
10842           } else if (bs != pSubSize) {
10843             /* Layout does not admit a pointwise block size */
10844             bs = 1;
10845           }
10846         }
10847       }
10848     }
10849     /* Must have same blocksize on all procs (some might have no points) */
10850     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10851     bsLocal[1] = bs;
10852     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10853     if (bsMinMax[0] != bsMinMax[1]) {
10854       bs = 1;
10855     } else {
10856       bs = bsMinMax[0];
10857     }
10858     PetscCall(PetscMalloc1(subSize, &subIndices));
10859     for (p = pStart; p < pEnd; ++p) {
10860       PetscInt gdof, goff;
10861 
10862       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10863       if (gdof > 0) {
10864         const PetscInt point = spmap[p];
10865 
10866         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10867         for (f = 0; f < Nf; ++f) {
10868           PetscInt fdof, fcdof, fc, f2, poff = 0;
10869 
10870           /* Can get rid of this loop by storing field information in the global section */
10871           for (f2 = 0; f2 < f; ++f2) {
10872             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10873             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10874             poff += fdof - fcdof;
10875           }
10876           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10877           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10878           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10879         }
10880       }
10881     }
10882     PetscCall(ISRestoreIndices(spIS, &spmap));
10883     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10884     if (bs > 1) {
10885       /* We need to check that the block size does not come from non-contiguous fields */
10886       PetscInt i, j, set = 1;
10887       for (i = 0; i < subSize; i += bs) {
10888         for (j = 0; j < bs; ++j) {
10889           if (subIndices[i + j] != subIndices[i] + j) {
10890             set = 0;
10891             break;
10892           }
10893         }
10894       }
10895       if (set) PetscCall(ISSetBlockSize(*is, bs));
10896     }
10897     // Attach nullspace
10898     if (dm->nullspaceConstructors) {
10899       for (f = 0; f < Nf; ++f) {
10900         (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10901         if ((*subdm)->nullspaceConstructors[f]) break;
10902       }
10903       if (f < Nf) {
10904         MatNullSpace nullSpace;
10905         PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10906 
10907         PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10908         PetscCall(MatNullSpaceDestroy(&nullSpace));
10909       }
10910     }
10911   }
10912   PetscFunctionReturn(PETSC_SUCCESS);
10913 }
10914 
10915 /*@
10916   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10917 
10918   Input Parameters:
10919 + dm    - The `DM`
10920 - dummy - unused argument
10921 
10922   Options Database Key:
10923 . -dm_plex_monitor_throughput - Activate the monitor
10924 
10925   Level: developer
10926 
10927 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10928 @*/
10929 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10930 {
10931   PetscLogHandler default_handler;
10932 
10933   PetscFunctionBegin;
10934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10935   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10936   if (default_handler) {
10937     PetscLogEvent      event;
10938     PetscEventPerfInfo eventInfo;
10939     PetscLogDouble     cellRate, flopRate;
10940     PetscInt           cStart, cEnd, Nf, N;
10941     const char        *name;
10942 
10943     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10944     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10945     PetscCall(DMGetNumFields(dm, &Nf));
10946     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10947     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10948     N        = (cEnd - cStart) * Nf * eventInfo.count;
10949     flopRate = eventInfo.flops / eventInfo.time;
10950     cellRate = N / eventInfo.time;
10951     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));
10952   } else {
10953     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.");
10954   }
10955   PetscFunctionReturn(PETSC_SUCCESS);
10956 }
10957