xref: /petsc/src/dm/impls/plex/plex.c (revision 2ff79c18c26c94ed8cb599682f680f231dca6444)
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       const char *vecname;
781       PetscInt    n, nroots;
782 
783       PetscCheck(dm->sfNatural, comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
784       PetscCall(VecGetLocalSize(originalv, &n));
785       PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
786       PetscCheck(n == nroots, comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
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 v = originalv;
793   } else v = originalv;
794 
795   if (ishdf5) {
796 #if defined(PETSC_HAVE_HDF5)
797     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
798 #else
799     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
800 #endif
801   } else if (isvtk) {
802     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
803   } else {
804     PetscBool isseq;
805 
806     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
807     if (isseq) PetscCall(VecView_Seq(v, viewer));
808     else PetscCall(VecView_MPI(v, viewer));
809   }
810   if (v != originalv) PetscCall(VecDestroy(&v));
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
815 {
816   DM        dm;
817   PetscBool ishdf5;
818 
819   PetscFunctionBegin;
820   PetscCall(VecGetDM(v, &dm));
821   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
822   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
823   if (ishdf5) {
824     DM          dmBC;
825     Vec         gv;
826     const char *name;
827 
828     PetscCall(DMGetOutputDM(dm, &dmBC));
829     PetscCall(DMGetGlobalVector(dmBC, &gv));
830     PetscCall(PetscObjectGetName((PetscObject)v, &name));
831     PetscCall(PetscObjectSetName((PetscObject)gv, name));
832     PetscCall(VecLoad_Default(gv, viewer));
833     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
834     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
835     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
836   } else PetscCall(VecLoad_Default(v, viewer));
837   PetscFunctionReturn(PETSC_SUCCESS);
838 }
839 
840 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
841 {
842   DM        dm;
843   PetscBool ishdf5, isexodusii, iscgns;
844 
845   PetscFunctionBegin;
846   PetscCall(VecGetDM(v, &dm));
847   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
848   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
849   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
850   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
851   if (ishdf5) {
852 #if defined(PETSC_HAVE_HDF5)
853     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
854 #else
855     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
856 #endif
857   } else if (isexodusii) {
858 #if defined(PETSC_HAVE_EXODUSII)
859     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
860 #else
861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
862 #endif
863   } else if (iscgns) {
864 #if defined(PETSC_HAVE_CGNS)
865     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
866 #else
867     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
868 #endif
869   } else PetscCall(VecLoad_Default(v, viewer));
870   PetscFunctionReturn(PETSC_SUCCESS);
871 }
872 
873 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
874 {
875   DM                dm;
876   PetscViewerFormat format;
877   PetscBool         ishdf5;
878 
879   PetscFunctionBegin;
880   PetscCall(VecGetDM(originalv, &dm));
881   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
882   PetscCall(PetscViewerGetFormat(viewer, &format));
883   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
884   if (format == PETSC_VIEWER_NATIVE) {
885     if (dm->useNatural) {
886       if (dm->sfNatural) {
887         if (ishdf5) {
888 #if defined(PETSC_HAVE_HDF5)
889           Vec         v;
890           const char *vecname;
891 
892           PetscCall(DMPlexCreateNaturalVector(dm, &v));
893           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
894           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
895           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
896           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
897           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
898           PetscCall(VecDestroy(&v));
899 #else
900           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
901 #endif
902         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
903       }
904     } else PetscCall(VecLoad_Default(originalv, viewer));
905   }
906   PetscFunctionReturn(PETSC_SUCCESS);
907 }
908 
909 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
910 {
911   PetscSection       coordSection;
912   Vec                coordinates;
913   DMLabel            depthLabel, celltypeLabel;
914   const char        *name[4];
915   const PetscScalar *a;
916   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
917 
918   PetscFunctionBegin;
919   PetscCall(DMGetDimension(dm, &dim));
920   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
921   PetscCall(DMGetCoordinateSection(dm, &coordSection));
922   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
923   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
924   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
925   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
926   PetscCall(VecGetArrayRead(coordinates, &a));
927   name[0]       = "vertex";
928   name[1]       = "edge";
929   name[dim - 1] = "face";
930   name[dim]     = "cell";
931   for (c = cStart; c < cEnd; ++c) {
932     PetscInt *closure = NULL;
933     PetscInt  closureSize, cl, ct;
934 
935     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
936     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
937     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
938     PetscCall(PetscViewerASCIIPushTab(viewer));
939     for (cl = 0; cl < closureSize * 2; cl += 2) {
940       PetscInt point = closure[cl], depth, dof, off, d, p;
941 
942       if ((point < pStart) || (point >= pEnd)) continue;
943       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
944       if (!dof) continue;
945       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
946       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
947       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
948       for (p = 0; p < dof / dim; ++p) {
949         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
950         for (d = 0; d < dim; ++d) {
951           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
952           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
953         }
954         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
955       }
956       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
957     }
958     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
959     PetscCall(PetscViewerASCIIPopTab(viewer));
960   }
961   PetscCall(VecRestoreArrayRead(coordinates, &a));
962   PetscFunctionReturn(PETSC_SUCCESS);
963 }
964 
965 typedef enum {
966   CS_CARTESIAN,
967   CS_POLAR,
968   CS_CYLINDRICAL,
969   CS_SPHERICAL
970 } CoordSystem;
971 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
972 
973 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
974 {
975   PetscInt i;
976 
977   PetscFunctionBegin;
978   if (dim > 3) {
979     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
980   } else {
981     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
982 
983     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
984     switch (cs) {
985     case CS_CARTESIAN:
986       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
987       break;
988     case CS_POLAR:
989       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
990       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
991       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
992       break;
993     case CS_CYLINDRICAL:
994       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
995       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
996       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
997       trcoords[2] = coords[2];
998       break;
999     case CS_SPHERICAL:
1000       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
1001       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1002       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1003       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1004       break;
1005     }
1006     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1007   }
1008   PetscFunctionReturn(PETSC_SUCCESS);
1009 }
1010 
1011 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1012 {
1013   DM_Plex          *mesh = (DM_Plex *)dm->data;
1014   DM                cdm, cdmCell;
1015   PetscSection      coordSection, coordSectionCell;
1016   Vec               coordinates, coordinatesCell;
1017   PetscViewerFormat format;
1018 
1019   PetscFunctionBegin;
1020   PetscCall(PetscViewerGetFormat(viewer, &format));
1021   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1022     const char *name;
1023     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1024     PetscInt    pStart, pEnd, p, numLabels, l;
1025     PetscMPIInt rank, size;
1026 
1027     PetscCall(DMGetCoordinateDM(dm, &cdm));
1028     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1029     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1030     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1031     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1032     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1033     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1034     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1035     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1036     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1037     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1038     PetscCall(DMGetDimension(dm, &dim));
1039     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1040     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1041     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1042     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1043     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1044     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1045     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1046     for (p = pStart; p < pEnd; ++p) {
1047       PetscInt dof, off, s;
1048 
1049       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1050       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1051       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1052     }
1053     PetscCall(PetscViewerFlush(viewer));
1054     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1055     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1056     for (p = pStart; p < pEnd; ++p) {
1057       PetscInt dof, off, c;
1058 
1059       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1060       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1061       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]));
1062     }
1063     PetscCall(PetscViewerFlush(viewer));
1064     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1065     if (coordSection && coordinates) {
1066       CoordSystem        cs = CS_CARTESIAN;
1067       const PetscScalar *array, *arrayCell = NULL;
1068       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1069       PetscMPIInt        rank;
1070       const char        *name;
1071 
1072       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1073       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1074       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1075       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1076       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1077       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1078       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1079       pStart = PetscMin(pvStart, pcStart);
1080       pEnd   = PetscMax(pvEnd, pcEnd);
1081       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1082       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1083       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1084       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1085 
1086       PetscCall(VecGetArrayRead(coordinates, &array));
1087       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1088       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1089       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1090       for (p = pStart; p < pEnd; ++p) {
1091         PetscInt dof, off;
1092 
1093         if (p >= pvStart && p < pvEnd) {
1094           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1095           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1096           if (dof) {
1097             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1098             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1099             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1100           }
1101         }
1102         if (cdmCell && p >= pcStart && p < pcEnd) {
1103           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1104           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1105           if (dof) {
1106             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1107             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1108             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1109           }
1110         }
1111       }
1112       PetscCall(PetscViewerFlush(viewer));
1113       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1114       PetscCall(VecRestoreArrayRead(coordinates, &array));
1115       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1116     }
1117     PetscCall(DMGetNumLabels(dm, &numLabels));
1118     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1119     for (l = 0; l < numLabels; ++l) {
1120       DMLabel     label;
1121       PetscBool   isdepth;
1122       const char *name;
1123 
1124       PetscCall(DMGetLabelName(dm, l, &name));
1125       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1126       if (isdepth) continue;
1127       PetscCall(DMGetLabel(dm, name, &label));
1128       PetscCall(DMLabelView(label, viewer));
1129     }
1130     if (size > 1) {
1131       PetscSF sf;
1132 
1133       PetscCall(DMGetPointSF(dm, &sf));
1134       PetscCall(PetscSFView(sf, viewer));
1135     }
1136     if (mesh->periodic.face_sfs)
1137       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1138     PetscCall(PetscViewerFlush(viewer));
1139   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1140     const char  *name, *color;
1141     const char  *defcolors[3]  = {"gray", "orange", "green"};
1142     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1143     char         lname[PETSC_MAX_PATH_LEN];
1144     PetscReal    scale      = 2.0;
1145     PetscReal    tikzscale  = 1.0;
1146     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1147     double       tcoords[3];
1148     PetscScalar *coords;
1149     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;
1150     PetscMPIInt  rank, size;
1151     char       **names, **colors, **lcolors;
1152     PetscBool    flg, lflg;
1153     PetscBT      wp = NULL;
1154     PetscInt     pEnd, pStart;
1155 
1156     PetscCall(DMGetCoordinateDM(dm, &cdm));
1157     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1158     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1159     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1160     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1161     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1162     PetscCall(DMGetDimension(dm, &dim));
1163     PetscCall(DMPlexGetDepth(dm, &depth));
1164     PetscCall(DMGetNumLabels(dm, &numLabels));
1165     numLabels  = PetscMax(numLabels, 10);
1166     numColors  = 10;
1167     numLColors = 10;
1168     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1169     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1170     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1171     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1172     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1173     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1174     n = 4;
1175     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1176     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1177     n = 4;
1178     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1179     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1180     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1181     if (!useLabels) numLabels = 0;
1182     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1183     if (!useColors) {
1184       numColors = 3;
1185       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1186     }
1187     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1188     if (!useColors) {
1189       numLColors = 4;
1190       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1191     }
1192     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1193     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1194     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1195     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1196     if (depth < dim) plotEdges = PETSC_FALSE;
1197     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1198 
1199     /* filter points with labelvalue != labeldefaultvalue */
1200     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1201     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1202     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1203     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1204     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1205     if (lflg) {
1206       DMLabel lbl;
1207 
1208       PetscCall(DMGetLabel(dm, lname, &lbl));
1209       if (lbl) {
1210         PetscInt val, defval;
1211 
1212         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1213         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1214         for (c = pStart; c < pEnd; c++) {
1215           PetscInt *closure = NULL;
1216           PetscInt  closureSize;
1217 
1218           PetscCall(DMLabelGetValue(lbl, c, &val));
1219           if (val == defval) continue;
1220 
1221           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1223           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224         }
1225       }
1226     }
1227 
1228     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1229     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1230     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1231     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1232 \\documentclass[tikz]{standalone}\n\n\
1233 \\usepackage{pgflibraryshapes}\n\
1234 \\usetikzlibrary{backgrounds}\n\
1235 \\usetikzlibrary{arrows}\n\
1236 \\begin{document}\n"));
1237     if (size > 1) {
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1239       for (p = 0; p < size; ++p) {
1240         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1241         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1242       }
1243       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1244     }
1245     if (drawHasse) {
1246       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1247 
1248       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1249       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1264     }
1265     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1266 
1267     /* Plot vertices */
1268     PetscCall(VecGetArray(coordinates, &coords));
1269     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1270     for (v = vStart; v < vEnd; ++v) {
1271       PetscInt  off, dof, d;
1272       PetscBool isLabeled = PETSC_FALSE;
1273 
1274       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1275       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1276       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1277       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1278       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1279       for (d = 0; d < dof; ++d) {
1280         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1281         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1282       }
1283       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1284       if (dim == 3) {
1285         PetscReal tmp = tcoords[1];
1286         tcoords[1]    = tcoords[2];
1287         tcoords[2]    = -tmp;
1288       }
1289       for (d = 0; d < dof; ++d) {
1290         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1291         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1292       }
1293       if (drawHasse) color = colors[0 % numColors];
1294       else color = colors[rank % numColors];
1295       for (l = 0; l < numLabels; ++l) {
1296         PetscInt val;
1297         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1298         if (val >= 0) {
1299           color     = lcolors[l % numLColors];
1300           isLabeled = PETSC_TRUE;
1301           break;
1302         }
1303       }
1304       if (drawNumbers[0]) {
1305         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1306       } else if (drawColors[0]) {
1307         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1308       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1309     }
1310     PetscCall(VecRestoreArray(coordinates, &coords));
1311     PetscCall(PetscViewerFlush(viewer));
1312     /* Plot edges */
1313     if (plotEdges) {
1314       PetscCall(VecGetArray(coordinates, &coords));
1315       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1316       for (e = eStart; e < eEnd; ++e) {
1317         const PetscInt *cone;
1318         PetscInt        coneSize, offA, offB, dof, d;
1319 
1320         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1321         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1322         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1323         PetscCall(DMPlexGetCone(dm, e, &cone));
1324         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1325         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1326         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1327         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1328         for (d = 0; d < dof; ++d) {
1329           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1330           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1331         }
1332         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1333         if (dim == 3) {
1334           PetscReal tmp = tcoords[1];
1335           tcoords[1]    = tcoords[2];
1336           tcoords[2]    = -tmp;
1337         }
1338         for (d = 0; d < dof; ++d) {
1339           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1340           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1341         }
1342         if (drawHasse) color = colors[1 % numColors];
1343         else color = colors[rank % numColors];
1344         for (l = 0; l < numLabels; ++l) {
1345           PetscInt val;
1346           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1347           if (val >= 0) {
1348             color = lcolors[l % numLColors];
1349             break;
1350           }
1351         }
1352         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1353       }
1354       PetscCall(VecRestoreArray(coordinates, &coords));
1355       PetscCall(PetscViewerFlush(viewer));
1356       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1357     }
1358     /* Plot cells */
1359     if (dim == 3 || !drawNumbers[1]) {
1360       for (e = eStart; e < eEnd; ++e) {
1361         const PetscInt *cone;
1362 
1363         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1364         color = colors[rank % numColors];
1365         for (l = 0; l < numLabels; ++l) {
1366           PetscInt val;
1367           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1368           if (val >= 0) {
1369             color = lcolors[l % numLColors];
1370             break;
1371           }
1372         }
1373         PetscCall(DMPlexGetCone(dm, e, &cone));
1374         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1375       }
1376     } else {
1377       DMPolytopeType ct;
1378 
1379       /* Drawing a 2D polygon */
1380       for (c = cStart; c < cEnd; ++c) {
1381         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1382         PetscCall(DMPlexGetCellType(dm, c, &ct));
1383         if (DMPolytopeTypeIsHybrid(ct)) {
1384           const PetscInt *cone;
1385           PetscInt        coneSize, e;
1386 
1387           PetscCall(DMPlexGetCone(dm, c, &cone));
1388           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1389           for (e = 0; e < coneSize; ++e) {
1390             const PetscInt *econe;
1391 
1392             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1393             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));
1394           }
1395         } else {
1396           PetscInt *closure = NULL;
1397           PetscInt  closureSize, Nv = 0, v;
1398 
1399           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1400           for (p = 0; p < closureSize * 2; p += 2) {
1401             const PetscInt point = closure[p];
1402 
1403             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1404           }
1405           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1406           for (v = 0; v <= Nv; ++v) {
1407             const PetscInt vertex = closure[v % Nv];
1408 
1409             if (v > 0) {
1410               if (plotEdges) {
1411                 const PetscInt *edge;
1412                 PetscInt        endpoints[2], ne;
1413 
1414                 endpoints[0] = closure[v - 1];
1415                 endpoints[1] = vertex;
1416                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1417                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1418                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1419                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1420               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1421             }
1422             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1423           }
1424           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1425           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1426         }
1427       }
1428     }
1429     for (c = cStart; c < cEnd; ++c) {
1430       double             ccoords[3] = {0.0, 0.0, 0.0};
1431       PetscBool          isLabeled  = PETSC_FALSE;
1432       PetscScalar       *cellCoords = NULL;
1433       const PetscScalar *array;
1434       PetscInt           numCoords, cdim, d;
1435       PetscBool          isDG;
1436 
1437       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1438       PetscCall(DMGetCoordinateDim(dm, &cdim));
1439       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1440       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1441       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1442       for (p = 0; p < numCoords / cdim; ++p) {
1443         for (d = 0; d < cdim; ++d) {
1444           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1445           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1446         }
1447         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1448         if (cdim == 3) {
1449           PetscReal tmp = tcoords[1];
1450           tcoords[1]    = tcoords[2];
1451           tcoords[2]    = -tmp;
1452         }
1453         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1454       }
1455       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1456       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1457       for (d = 0; d < cdim; ++d) {
1458         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1459         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1460       }
1461       if (drawHasse) color = colors[depth % numColors];
1462       else color = colors[rank % numColors];
1463       for (l = 0; l < numLabels; ++l) {
1464         PetscInt val;
1465         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1466         if (val >= 0) {
1467           color     = lcolors[l % numLColors];
1468           isLabeled = PETSC_TRUE;
1469           break;
1470         }
1471       }
1472       if (drawNumbers[dim]) {
1473         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1474       } else if (drawColors[dim]) {
1475         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1476       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1477     }
1478     if (drawHasse) {
1479       int height = 0;
1480 
1481       color = colors[depth % numColors];
1482       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1483       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1485       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1487 
1488       if (depth > 2) {
1489         color = colors[1 % numColors];
1490         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1491         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1495       }
1496 
1497       color = colors[1 % numColors];
1498       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1499       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1501       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1502       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1503 
1504       color = colors[0 % numColors];
1505       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1506       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1508       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1509       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1510 
1511       for (p = pStart; p < pEnd; ++p) {
1512         const PetscInt *cone;
1513         PetscInt        coneSize, cp;
1514 
1515         PetscCall(DMPlexGetCone(dm, p, &cone));
1516         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1517         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1518       }
1519     }
1520     PetscCall(PetscViewerFlush(viewer));
1521     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1522     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1523     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1524     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1525     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1526     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1527     PetscCall(PetscFree3(names, colors, lcolors));
1528     PetscCall(PetscBTDestroy(&wp));
1529   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1530     Vec                    cown, acown;
1531     VecScatter             sct;
1532     ISLocalToGlobalMapping g2l;
1533     IS                     gid, acis;
1534     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1535     MPI_Group              ggroup, ngroup;
1536     PetscScalar           *array, nid;
1537     const PetscInt        *idxs;
1538     PetscInt              *idxs2, *start, *adjacency, *work;
1539     PetscInt64             lm[3], gm[3];
1540     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1541     PetscMPIInt            d1, d2, rank;
1542 
1543     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1544     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1545 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1546     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1547 #endif
1548     if (ncomm != MPI_COMM_NULL) {
1549       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1550       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1551       d1 = 0;
1552       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1553       nid = d2;
1554       PetscCallMPI(MPI_Group_free(&ggroup));
1555       PetscCallMPI(MPI_Group_free(&ngroup));
1556       PetscCallMPI(MPI_Comm_free(&ncomm));
1557     } else nid = 0.0;
1558 
1559     /* Get connectivity */
1560     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1561     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1562 
1563     /* filter overlapped local cells */
1564     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1565     PetscCall(ISGetIndices(gid, &idxs));
1566     PetscCall(ISGetLocalSize(gid, &cum));
1567     PetscCall(PetscMalloc1(cum, &idxs2));
1568     for (c = cStart, cum = 0; c < cEnd; c++) {
1569       if (idxs[c - cStart] < 0) continue;
1570       idxs2[cum++] = idxs[c - cStart];
1571     }
1572     PetscCall(ISRestoreIndices(gid, &idxs));
1573     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1574     PetscCall(ISDestroy(&gid));
1575     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1576 
1577     /* support for node-aware cell locality */
1578     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1579     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1580     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1581     PetscCall(VecGetArray(cown, &array));
1582     for (c = 0; c < numVertices; c++) array[c] = nid;
1583     PetscCall(VecRestoreArray(cown, &array));
1584     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1585     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1586     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1587     PetscCall(ISDestroy(&acis));
1588     PetscCall(VecScatterDestroy(&sct));
1589     PetscCall(VecDestroy(&cown));
1590 
1591     /* compute edgeCut */
1592     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1593     PetscCall(PetscMalloc1(cum, &work));
1594     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1595     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1596     PetscCall(ISDestroy(&gid));
1597     PetscCall(VecGetArray(acown, &array));
1598     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1599       PetscInt totl;
1600 
1601       totl = start[c + 1] - start[c];
1602       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1603       for (i = 0; i < totl; i++) {
1604         if (work[i] < 0) {
1605           ect += 1;
1606           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1607         }
1608       }
1609     }
1610     PetscCall(PetscFree(work));
1611     PetscCall(VecRestoreArray(acown, &array));
1612     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1613     lm[1] = -numVertices;
1614     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1615     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1616     lm[0] = ect;                     /* edgeCut */
1617     lm[1] = ectn;                    /* node-aware edgeCut */
1618     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1619     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1620     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1621 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1622     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1623 #else
1624     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1625 #endif
1626     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1627     PetscCall(PetscFree(start));
1628     PetscCall(PetscFree(adjacency));
1629     PetscCall(VecDestroy(&acown));
1630   } else {
1631     const char    *name;
1632     PetscInt      *sizes, *hybsizes, *ghostsizes;
1633     PetscInt       locDepth, depth, cellHeight, dim, d;
1634     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1635     PetscInt       numLabels, l, maxSize = 17;
1636     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1637     MPI_Comm       comm;
1638     PetscMPIInt    size, rank;
1639 
1640     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1641     PetscCallMPI(MPI_Comm_size(comm, &size));
1642     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1643     PetscCall(DMGetDimension(dm, &dim));
1644     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1645     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1646     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1647     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1648     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1649     PetscCall(DMPlexGetDepth(dm, &locDepth));
1650     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1651     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1652     gcNum = gcEnd - gcStart;
1653     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1654     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1655     for (d = 0; d <= depth; d++) {
1656       PetscInt Nc[2] = {0, 0}, ict;
1657 
1658       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1659       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1660       ict = ct0;
1661       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1662       ct0 = (DMPolytopeType)ict;
1663       for (p = pStart; p < pEnd; ++p) {
1664         DMPolytopeType ct;
1665 
1666         PetscCall(DMPlexGetCellType(dm, p, &ct));
1667         if (ct == ct0) ++Nc[0];
1668         else ++Nc[1];
1669       }
1670       if (size < maxSize) {
1671         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1672         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1673         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1674         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1675         for (p = 0; p < size; ++p) {
1676           if (rank == 0) {
1677             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1678             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1679             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1680           }
1681         }
1682       } else {
1683         PetscInt locMinMax[2];
1684 
1685         locMinMax[0] = Nc[0] + Nc[1];
1686         locMinMax[1] = Nc[0] + Nc[1];
1687         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1688         locMinMax[0] = Nc[1];
1689         locMinMax[1] = Nc[1];
1690         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1691         if (d == depth) {
1692           locMinMax[0] = gcNum;
1693           locMinMax[1] = gcNum;
1694           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1695         }
1696         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1697         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1698         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1699         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1700       }
1701       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1702     }
1703     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1704     {
1705       const PetscReal *maxCell;
1706       const PetscReal *L;
1707       PetscBool        localized;
1708 
1709       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1710       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1711       if (L || localized) {
1712         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1713         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1714         if (L) {
1715           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1716           for (d = 0; d < dim; ++d) {
1717             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1718             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1719           }
1720           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1721         }
1722         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1723         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1724       }
1725     }
1726     PetscCall(DMGetNumLabels(dm, &numLabels));
1727     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1728     for (l = 0; l < numLabels; ++l) {
1729       DMLabel     label;
1730       const char *name;
1731       PetscInt   *values;
1732       PetscInt    numValues, v;
1733 
1734       PetscCall(DMGetLabelName(dm, l, &name));
1735       PetscCall(DMGetLabel(dm, name, &label));
1736       PetscCall(DMLabelGetNumValues(label, &numValues));
1737       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1738 
1739       { // Extract array of DMLabel values so it can be sorted
1740         IS              is_values;
1741         const PetscInt *is_values_local = NULL;
1742 
1743         PetscCall(DMLabelGetValueIS(label, &is_values));
1744         PetscCall(ISGetIndices(is_values, &is_values_local));
1745         PetscCall(PetscMalloc1(numValues, &values));
1746         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1747         PetscCall(PetscSortInt(numValues, values));
1748         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1749         PetscCall(ISDestroy(&is_values));
1750       }
1751       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1752       for (v = 0; v < numValues; ++v) {
1753         PetscInt size;
1754 
1755         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1756         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1757         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1758       }
1759       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1760       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1761       PetscCall(PetscFree(values));
1762     }
1763     {
1764       char    **labelNames;
1765       PetscInt  Nl = numLabels;
1766       PetscBool flg;
1767 
1768       PetscCall(PetscMalloc1(Nl, &labelNames));
1769       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1770       for (l = 0; l < Nl; ++l) {
1771         DMLabel label;
1772 
1773         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1774         if (flg) {
1775           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1776           PetscCall(DMLabelView(label, viewer));
1777         }
1778         PetscCall(PetscFree(labelNames[l]));
1779       }
1780       PetscCall(PetscFree(labelNames));
1781     }
1782     /* If no fields are specified, people do not want to see adjacency */
1783     if (dm->Nf) {
1784       PetscInt f;
1785 
1786       for (f = 0; f < dm->Nf; ++f) {
1787         const char *name;
1788 
1789         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1790         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1791         PetscCall(PetscViewerASCIIPushTab(viewer));
1792         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1793         if (dm->fields[f].adjacency[0]) {
1794           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1795           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1796         } else {
1797           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1798           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1799         }
1800         PetscCall(PetscViewerASCIIPopTab(viewer));
1801       }
1802     }
1803     DMPlexTransform tr;
1804 
1805     PetscCall(DMPlexGetTransform(dm, &tr));
1806     if (tr) {
1807       PetscCall(PetscViewerASCIIPushTab(viewer));
1808       PetscCall(PetscViewerASCIIPrintf(viewer, "Created using transform:\n"));
1809       PetscCall(DMPlexTransformView(tr, viewer));
1810       PetscCall(PetscViewerASCIIPopTab(viewer));
1811     }
1812     PetscCall(DMGetCoarseDM(dm, &cdm));
1813     if (cdm) {
1814       PetscCall(PetscViewerASCIIPushTab(viewer));
1815       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1816       PetscCall(DMPlexView_Ascii(cdm, viewer));
1817       PetscCall(PetscViewerASCIIPopTab(viewer));
1818     }
1819   }
1820   PetscFunctionReturn(PETSC_SUCCESS);
1821 }
1822 
1823 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt lC, PetscInt cC, PetscInt cell, const PetscScalar coords[])
1824 {
1825   DMPolytopeType ct;
1826   PetscMPIInt    rank;
1827   PetscInt       cdim;
1828   int            lineColor, cellColor;
1829 
1830   PetscFunctionBegin;
1831   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1832   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1833   PetscCall(DMGetCoordinateDim(dm, &cdim));
1834   lineColor = (int)(lC < 0 ? PETSC_DRAW_BLACK : lC);
1835   cellColor = (int)(cC < 0 ? PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2 : cC);
1836   switch (ct) {
1837   case DM_POLYTOPE_SEGMENT:
1838   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1839     switch (cdim) {
1840     case 1: {
1841       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1842       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1843 
1844       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, lineColor));
1845       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, lineColor));
1846       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, lineColor));
1847     } break;
1848     case 2: {
1849       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1850       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1851       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1852 
1853       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1854       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, lineColor));
1855       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, lineColor));
1856     } break;
1857     default:
1858       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1859     }
1860     break;
1861   case DM_POLYTOPE_TRIANGLE:
1862     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1863     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1864     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1865     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1866     break;
1867   case DM_POLYTOPE_QUADRILATERAL:
1868     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1869     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), cellColor, cellColor, cellColor));
1870     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1871     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1872     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1873     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1874     break;
1875   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1876     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1877     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1878     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1879     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1880     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1881     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1882     break;
1883   case DM_POLYTOPE_FV_GHOST:
1884     break;
1885   default:
1886     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1887   }
1888   PetscFunctionReturn(PETSC_SUCCESS);
1889 }
1890 
1891 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1892 {
1893   PetscReal   centroid[2] = {0., 0.};
1894   PetscMPIInt rank;
1895   PetscMPIInt fillColor;
1896 
1897   PetscFunctionBegin;
1898   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1899   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1900   for (PetscInt v = 0; v < Nv; ++v) {
1901     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1902     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1903   }
1904   for (PetscInt e = 0; e < Nv; ++e) {
1905     refCoords[0] = refVertices[e * 2 + 0];
1906     refCoords[1] = refVertices[e * 2 + 1];
1907     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1908       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1909       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1910     }
1911     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1912     for (PetscInt d = 0; d < edgeDiv; ++d) {
1913       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));
1914       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1915     }
1916   }
1917   PetscFunctionReturn(PETSC_SUCCESS);
1918 }
1919 
1920 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1921 {
1922   DMPolytopeType ct;
1923 
1924   PetscFunctionBegin;
1925   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1926   switch (ct) {
1927   case DM_POLYTOPE_TRIANGLE: {
1928     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1929 
1930     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1931   } break;
1932   case DM_POLYTOPE_QUADRILATERAL: {
1933     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1934 
1935     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1936   } break;
1937   default:
1938     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1939   }
1940   PetscFunctionReturn(PETSC_SUCCESS);
1941 }
1942 
1943 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1944 {
1945   PetscDraw    draw;
1946   DM           cdm;
1947   PetscSection coordSection;
1948   Vec          coordinates;
1949   PetscReal    xyl[3], xyr[3];
1950   PetscReal   *refCoords, *edgeCoords;
1951   PetscBool    isnull, drawAffine;
1952   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv, lineColor = PETSC_DETERMINE, cellColor = PETSC_DETERMINE;
1953 
1954   PetscFunctionBegin;
1955   PetscCall(DMGetCoordinateDim(dm, &dim));
1956   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1957   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1958   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1959   edgeDiv    = cDegree + 1;
1960   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_line_color", &lineColor, NULL));
1961   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_cell_color", &cellColor, NULL));
1962   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1963   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1964   PetscCall(DMGetCoordinateDM(dm, &cdm));
1965   PetscCall(DMGetLocalSection(cdm, &coordSection));
1966   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1967   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1968   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1969 
1970   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1971   PetscCall(PetscDrawIsNull(draw, &isnull));
1972   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1973   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1974 
1975   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1976   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1977   PetscCall(PetscDrawClear(draw));
1978 
1979   for (c = cStart; c < cEnd; ++c) {
1980     PetscScalar       *coords = NULL;
1981     const PetscScalar *coords_arr;
1982     PetscInt           numCoords;
1983     PetscBool          isDG;
1984 
1985     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1986     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, lineColor, cellColor, c, coords));
1987     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1988     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1989   }
1990   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1991   PetscCall(PetscDrawFlush(draw));
1992   PetscCall(PetscDrawPause(draw));
1993   PetscCall(PetscDrawSave(draw));
1994   PetscFunctionReturn(PETSC_SUCCESS);
1995 }
1996 
1997 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1998 {
1999   DM           odm = dm, rdm = dm, cdm;
2000   PetscFE      fe;
2001   PetscSpace   sp;
2002   PetscClassId id;
2003   PetscInt     degree;
2004   PetscBool    hoView = PETSC_TRUE;
2005 
2006   PetscFunctionBegin;
2007   PetscObjectOptionsBegin((PetscObject)dm);
2008   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2009   PetscOptionsEnd();
2010   PetscCall(PetscObjectReference((PetscObject)dm));
2011   *hdm = dm;
2012   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2013   PetscCall(DMGetCoordinateDM(dm, &cdm));
2014   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2015   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2016   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2017   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2018   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2019   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2020     DM  cdm, rcdm;
2021     Mat In;
2022     Vec cl, rcl;
2023 
2024     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2025     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, PETSC_FALSE));
2026     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2027     PetscCall(DMGetCoordinateDM(odm, &cdm));
2028     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2029     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2030     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2031     PetscCall(DMSetCoarseDM(rcdm, cdm));
2032     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2033     PetscCall(MatMult(In, cl, rcl));
2034     PetscCall(MatDestroy(&In));
2035     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2036     PetscCall(DMDestroy(&odm));
2037     odm = rdm;
2038   }
2039   *hdm = rdm;
2040   PetscFunctionReturn(PETSC_SUCCESS);
2041 }
2042 
2043 #if defined(PETSC_HAVE_EXODUSII)
2044   #include <exodusII.h>
2045   #include <petscviewerexodusii.h>
2046 #endif
2047 
2048 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2049 {
2050   PetscBool isascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2051   char      name[PETSC_MAX_PATH_LEN];
2052 
2053   PetscFunctionBegin;
2054   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2055   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2056   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
2057   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2060   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2061   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2063   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2064   if (isascii) {
2065     PetscViewerFormat format;
2066     PetscCall(PetscViewerGetFormat(viewer, &format));
2067     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2068     else PetscCall(DMPlexView_Ascii(dm, viewer));
2069   } else if (ishdf5) {
2070 #if defined(PETSC_HAVE_HDF5)
2071     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   } else if (isvtk) {
2076     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2077   } else if (isdraw) {
2078     DM hdm;
2079 
2080     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2081     PetscCall(DMPlexView_Draw(hdm, viewer));
2082     PetscCall(DMDestroy(&hdm));
2083   } else if (isglvis) {
2084     PetscCall(DMPlexView_GLVis(dm, viewer));
2085 #if defined(PETSC_HAVE_EXODUSII)
2086   } else if (isexodus) {
2087     /*
2088       ExodusII requires that all sets be part of exactly one cell set.
2089       If the dm does not have a "Cell Sets" label defined, we create one
2090       with ID 1, containing all cells.
2091       Note that if the Cell Sets label is defined but does not cover all cells,
2092       we may still have a problem. This should probably be checked here or in the viewer;
2093     */
2094     PetscInt numCS;
2095     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2096     if (!numCS) {
2097       PetscInt cStart, cEnd, c;
2098       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2099       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2100       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2101     }
2102     PetscCall(DMView_PlexExodusII(dm, viewer));
2103 #endif
2104 #if defined(PETSC_HAVE_CGNS)
2105   } else if (iscgns) {
2106     PetscCall(DMView_PlexCGNS(dm, viewer));
2107 #endif
2108   } else if (ispython) {
2109     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2110   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2111   /* Optionally view the partition */
2112   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2113   if (flg) {
2114     Vec ranks;
2115     PetscCall(DMPlexCreateRankField(dm, &ranks));
2116     PetscCall(VecView(ranks, viewer));
2117     PetscCall(VecDestroy(&ranks));
2118   }
2119   /* Optionally view a label */
2120   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2121   if (flg) {
2122     DMLabel label;
2123     Vec     val;
2124 
2125     PetscCall(DMGetLabel(dm, name, &label));
2126     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2127     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2128     PetscCall(VecView(val, viewer));
2129     PetscCall(VecDestroy(&val));
2130   }
2131   PetscFunctionReturn(PETSC_SUCCESS);
2132 }
2133 
2134 /*@
2135   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2136 
2137   Collective
2138 
2139   Input Parameters:
2140 + dm     - The `DM` whose topology is to be saved
2141 - viewer - The `PetscViewer` to save it in
2142 
2143   Level: advanced
2144 
2145 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2146 @*/
2147 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2148 {
2149   PetscBool ishdf5;
2150 
2151   PetscFunctionBegin;
2152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2153   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2154   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2155   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2156   if (ishdf5) {
2157 #if defined(PETSC_HAVE_HDF5)
2158     IS                globalPointNumbering;
2159     PetscViewerFormat format;
2160 
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2163     PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2164     PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2165     PetscCall(ISDestroy(&globalPointNumbering));
2166 #else
2167     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2168 #endif
2169   }
2170   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2171   PetscFunctionReturn(PETSC_SUCCESS);
2172 }
2173 
2174 /*@
2175   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2176 
2177   Collective
2178 
2179   Input Parameters:
2180 + dm     - The `DM` whose coordinates are to be saved
2181 - viewer - The `PetscViewer` for saving
2182 
2183   Level: advanced
2184 
2185 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2186 @*/
2187 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2188 {
2189   PetscBool ishdf5;
2190 
2191   PetscFunctionBegin;
2192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2193   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2194   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2195   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2196   if (ishdf5) {
2197 #if defined(PETSC_HAVE_HDF5)
2198     PetscViewerFormat format;
2199 
2200     PetscCall(PetscViewerGetFormat(viewer, &format));
2201     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2202     PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2203 #else
2204     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2205 #endif
2206   }
2207   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2208   PetscFunctionReturn(PETSC_SUCCESS);
2209 }
2210 
2211 /*@
2212   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2213 
2214   Collective
2215 
2216   Input Parameters:
2217 + dm     - The `DM` whose labels are to be saved
2218 - viewer - The `PetscViewer` for saving
2219 
2220   Level: advanced
2221 
2222 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2223 @*/
2224 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2225 {
2226   PetscBool ishdf5;
2227 
2228   PetscFunctionBegin;
2229   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2230   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2231   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2232   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2233   if (ishdf5) {
2234 #if defined(PETSC_HAVE_HDF5)
2235     IS                globalPointNumbering;
2236     PetscViewerFormat format;
2237 
2238     PetscCall(PetscViewerGetFormat(viewer, &format));
2239     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2240     PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2241     PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2242     PetscCall(ISDestroy(&globalPointNumbering));
2243 #else
2244     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2245 #endif
2246   }
2247   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2248   PetscFunctionReturn(PETSC_SUCCESS);
2249 }
2250 
2251 /*@
2252   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2253 
2254   Collective
2255 
2256   Input Parameters:
2257 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2258 . viewer    - The `PetscViewer` for saving
2259 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2260 
2261   Level: advanced
2262 
2263   Notes:
2264   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.
2265 
2266   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.
2267 
2268 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2269 @*/
2270 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2271 {
2272   PetscBool ishdf5;
2273 
2274   PetscFunctionBegin;
2275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2276   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2277   if (!sectiondm) sectiondm = dm;
2278   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2279   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2280   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2281   if (ishdf5) {
2282 #if defined(PETSC_HAVE_HDF5)
2283     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2284 #else
2285     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2286 #endif
2287   }
2288   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2289   PetscFunctionReturn(PETSC_SUCCESS);
2290 }
2291 
2292 /*@
2293   DMPlexGlobalVectorView - Saves a global vector
2294 
2295   Collective
2296 
2297   Input Parameters:
2298 + dm        - The `DM` that represents the topology
2299 . viewer    - The `PetscViewer` to save data with
2300 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2301 - vec       - The global vector to be saved
2302 
2303   Level: advanced
2304 
2305   Notes:
2306   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.
2307 
2308   Calling sequence:
2309 .vb
2310        DMCreate(PETSC_COMM_WORLD, &dm);
2311        DMSetType(dm, DMPLEX);
2312        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2313        DMClone(dm, &sectiondm);
2314        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2315        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2316        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2317        PetscSectionSetChart(section, pStart, pEnd);
2318        PetscSectionSetUp(section);
2319        DMSetLocalSection(sectiondm, section);
2320        PetscSectionDestroy(&section);
2321        DMGetGlobalVector(sectiondm, &vec);
2322        PetscObjectSetName((PetscObject)vec, "vec_name");
2323        DMPlexTopologyView(dm, viewer);
2324        DMPlexSectionView(dm, viewer, sectiondm);
2325        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2326        DMRestoreGlobalVector(sectiondm, &vec);
2327        DMDestroy(&sectiondm);
2328        DMDestroy(&dm);
2329 .ve
2330 
2331 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2332 @*/
2333 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2334 {
2335   PetscBool ishdf5;
2336 
2337   PetscFunctionBegin;
2338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2339   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2340   if (!sectiondm) sectiondm = dm;
2341   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2342   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2343   /* Check consistency */
2344   {
2345     PetscSection section;
2346     PetscBool    includesConstraints;
2347     PetscInt     m, m1;
2348 
2349     PetscCall(VecGetLocalSize(vec, &m1));
2350     PetscCall(DMGetGlobalSection(sectiondm, &section));
2351     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2352     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2353     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2354     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2355   }
2356   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2357   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2358   if (ishdf5) {
2359 #if defined(PETSC_HAVE_HDF5)
2360     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2361 #else
2362     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2363 #endif
2364   }
2365   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2366   PetscFunctionReturn(PETSC_SUCCESS);
2367 }
2368 
2369 /*@
2370   DMPlexLocalVectorView - Saves a local vector
2371 
2372   Collective
2373 
2374   Input Parameters:
2375 + dm        - The `DM` that represents the topology
2376 . viewer    - The `PetscViewer` to save data with
2377 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2378 - vec       - The local vector to be saved
2379 
2380   Level: advanced
2381 
2382   Note:
2383   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.
2384 
2385   Calling sequence:
2386 .vb
2387        DMCreate(PETSC_COMM_WORLD, &dm);
2388        DMSetType(dm, DMPLEX);
2389        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2390        DMClone(dm, &sectiondm);
2391        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2392        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2393        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2394        PetscSectionSetChart(section, pStart, pEnd);
2395        PetscSectionSetUp(section);
2396        DMSetLocalSection(sectiondm, section);
2397        DMGetLocalVector(sectiondm, &vec);
2398        PetscObjectSetName((PetscObject)vec, "vec_name");
2399        DMPlexTopologyView(dm, viewer);
2400        DMPlexSectionView(dm, viewer, sectiondm);
2401        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2402        DMRestoreLocalVector(sectiondm, &vec);
2403        DMDestroy(&sectiondm);
2404        DMDestroy(&dm);
2405 .ve
2406 
2407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2408 @*/
2409 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2410 {
2411   PetscBool ishdf5;
2412 
2413   PetscFunctionBegin;
2414   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2415   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2416   if (!sectiondm) sectiondm = dm;
2417   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2418   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2419   /* Check consistency */
2420   {
2421     PetscSection section;
2422     PetscBool    includesConstraints;
2423     PetscInt     m, m1;
2424 
2425     PetscCall(VecGetLocalSize(vec, &m1));
2426     PetscCall(DMGetLocalSection(sectiondm, &section));
2427     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2428     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2429     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2430     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2431   }
2432   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2433   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2434   if (ishdf5) {
2435 #if defined(PETSC_HAVE_HDF5)
2436     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2437 #else
2438     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2439 #endif
2440   }
2441   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2442   PetscFunctionReturn(PETSC_SUCCESS);
2443 }
2444 
2445 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2446 {
2447   PetscBool ishdf5;
2448 
2449   PetscFunctionBegin;
2450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2451   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2453   if (ishdf5) {
2454 #if defined(PETSC_HAVE_HDF5)
2455     PetscViewerFormat format;
2456     PetscCall(PetscViewerGetFormat(viewer, &format));
2457     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2458       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2459     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2460       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2461     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2462     PetscFunctionReturn(PETSC_SUCCESS);
2463 #else
2464     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2465 #endif
2466   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2467 }
2468 
2469 /*@
2470   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2471 
2472   Collective
2473 
2474   Input Parameters:
2475 + dm     - The `DM` into which the topology is loaded
2476 - viewer - The `PetscViewer` for the saved topology
2477 
2478   Output Parameter:
2479 . 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;
2480   `NULL` if unneeded
2481 
2482   Level: advanced
2483 
2484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2485           `PetscViewer`, `PetscSF`
2486 @*/
2487 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2488 {
2489   PetscBool ishdf5;
2490 
2491   PetscFunctionBegin;
2492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2493   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2494   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2495   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2496   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2497   if (ishdf5) {
2498 #if defined(PETSC_HAVE_HDF5)
2499     PetscViewerFormat format;
2500 
2501     PetscCall(PetscViewerGetFormat(viewer, &format));
2502     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2503     PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2504 #else
2505     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2506 #endif
2507   }
2508   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2509   PetscFunctionReturn(PETSC_SUCCESS);
2510 }
2511 
2512 /*@
2513   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2514 
2515   Collective
2516 
2517   Input Parameters:
2518 + dm                   - The `DM` into which the coordinates are loaded
2519 . viewer               - The `PetscViewer` for the saved coordinates
2520 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2521 
2522   Level: advanced
2523 
2524 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2525           `PetscSF`, `PetscViewer`
2526 @*/
2527 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2528 {
2529   PetscBool ishdf5;
2530 
2531   PetscFunctionBegin;
2532   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2533   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2534   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2535   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2536   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2537   if (ishdf5) {
2538 #if defined(PETSC_HAVE_HDF5)
2539     PetscViewerFormat format;
2540 
2541     PetscCall(PetscViewerGetFormat(viewer, &format));
2542     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2543     PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2544 #else
2545     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2546 #endif
2547   }
2548   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2549   PetscFunctionReturn(PETSC_SUCCESS);
2550 }
2551 
2552 /*@
2553   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2554 
2555   Collective
2556 
2557   Input Parameters:
2558 + dm                   - The `DM` into which the labels are loaded
2559 . viewer               - The `PetscViewer` for the saved labels
2560 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2561 
2562   Level: advanced
2563 
2564   Note:
2565   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2566 
2567 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2568           `PetscSF`, `PetscViewer`
2569 @*/
2570 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2571 {
2572   PetscBool ishdf5;
2573 
2574   PetscFunctionBegin;
2575   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2576   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2577   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2579   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2580   if (ishdf5) {
2581 #if defined(PETSC_HAVE_HDF5)
2582     PetscViewerFormat format;
2583 
2584     PetscCall(PetscViewerGetFormat(viewer, &format));
2585     PetscCheck(format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2586     PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2587 #else
2588     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2589 #endif
2590   }
2591   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2592   PetscFunctionReturn(PETSC_SUCCESS);
2593 }
2594 
2595 /*@
2596   DMPlexSectionLoad - Loads section into a `DMPLEX`
2597 
2598   Collective
2599 
2600   Input Parameters:
2601 + dm                   - The `DM` that represents the topology
2602 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2603 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2604 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2605 
2606   Output Parameters:
2607 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2608 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2609 
2610   Level: advanced
2611 
2612   Notes:
2613   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2614 
2615   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2616 
2617   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2618 
2619   Example using 2 processes:
2620 .vb
2621   NX (number of points on dm): 4
2622   sectionA                   : the on-disk section
2623   vecA                       : a vector associated with sectionA
2624   sectionB                   : sectiondm's local section constructed in this function
2625   vecB (local)               : a vector associated with sectiondm's local section
2626   vecB (global)              : a vector associated with sectiondm's global section
2627 
2628                                      rank 0    rank 1
2629   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2630   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2631   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2632   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2633   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2634   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2635   sectionB->atlasDof             :     1 0 1 | 1 3
2636   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2637   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2638   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2639 .ve
2640   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2641 
2642 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2643 @*/
2644 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2645 {
2646   PetscBool ishdf5;
2647 
2648   PetscFunctionBegin;
2649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2650   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2651   if (!sectiondm) sectiondm = dm;
2652   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2653   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2654   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2655   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2657   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2658   if (ishdf5) {
2659 #if defined(PETSC_HAVE_HDF5)
2660     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2661 #else
2662     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2663 #endif
2664   }
2665   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2666   PetscFunctionReturn(PETSC_SUCCESS);
2667 }
2668 
2669 /*@
2670   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2671 
2672   Collective
2673 
2674   Input Parameters:
2675 + dm        - The `DM` that represents the topology
2676 . viewer    - The `PetscViewer` that represents the on-disk vector data
2677 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2678 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2679 - vec       - The global vector to set values of
2680 
2681   Level: advanced
2682 
2683   Notes:
2684   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2685 
2686   Calling sequence:
2687 .vb
2688        DMCreate(PETSC_COMM_WORLD, &dm);
2689        DMSetType(dm, DMPLEX);
2690        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2691        DMPlexTopologyLoad(dm, viewer, &sfX);
2692        DMClone(dm, &sectiondm);
2693        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2694        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2695        DMGetGlobalVector(sectiondm, &vec);
2696        PetscObjectSetName((PetscObject)vec, "vec_name");
2697        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2698        DMRestoreGlobalVector(sectiondm, &vec);
2699        PetscSFDestroy(&gsf);
2700        PetscSFDestroy(&sfX);
2701        DMDestroy(&sectiondm);
2702        DMDestroy(&dm);
2703 .ve
2704 
2705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2706           `PetscSF`, `PetscViewer`
2707 @*/
2708 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2709 {
2710   PetscBool ishdf5;
2711 
2712   PetscFunctionBegin;
2713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2714   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2715   if (!sectiondm) sectiondm = dm;
2716   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2717   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2718   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2719   /* Check consistency */
2720   {
2721     PetscSection section;
2722     PetscBool    includesConstraints;
2723     PetscInt     m, m1;
2724 
2725     PetscCall(VecGetLocalSize(vec, &m1));
2726     PetscCall(DMGetGlobalSection(sectiondm, &section));
2727     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2728     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2729     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2730     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2731   }
2732   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2733   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2734   if (ishdf5) {
2735 #if defined(PETSC_HAVE_HDF5)
2736     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2737 #else
2738     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2739 #endif
2740   }
2741   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2742   PetscFunctionReturn(PETSC_SUCCESS);
2743 }
2744 
2745 /*@
2746   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2747 
2748   Collective
2749 
2750   Input Parameters:
2751 + dm        - The `DM` that represents the topology
2752 . viewer    - The `PetscViewer` that represents the on-disk vector data
2753 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2754 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2755 - vec       - The local vector to set values of
2756 
2757   Level: advanced
2758 
2759   Notes:
2760   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2761 
2762   Calling sequence:
2763 .vb
2764        DMCreate(PETSC_COMM_WORLD, &dm);
2765        DMSetType(dm, DMPLEX);
2766        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2767        DMPlexTopologyLoad(dm, viewer, &sfX);
2768        DMClone(dm, &sectiondm);
2769        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2770        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2771        DMGetLocalVector(sectiondm, &vec);
2772        PetscObjectSetName((PetscObject)vec, "vec_name");
2773        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2774        DMRestoreLocalVector(sectiondm, &vec);
2775        PetscSFDestroy(&lsf);
2776        PetscSFDestroy(&sfX);
2777        DMDestroy(&sectiondm);
2778        DMDestroy(&dm);
2779 .ve
2780 
2781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2782           `PetscSF`, `PetscViewer`
2783 @*/
2784 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2785 {
2786   PetscBool ishdf5;
2787 
2788   PetscFunctionBegin;
2789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2790   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2791   if (!sectiondm) sectiondm = dm;
2792   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2793   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2794   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2795   /* Check consistency */
2796   {
2797     PetscSection section;
2798     PetscBool    includesConstraints;
2799     PetscInt     m, m1;
2800 
2801     PetscCall(VecGetLocalSize(vec, &m1));
2802     PetscCall(DMGetLocalSection(sectiondm, &section));
2803     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2804     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2805     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2806     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2807   }
2808   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2809   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2810   if (ishdf5) {
2811 #if defined(PETSC_HAVE_HDF5)
2812     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2813 #else
2814     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2815 #endif
2816   }
2817   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2818   PetscFunctionReturn(PETSC_SUCCESS);
2819 }
2820 
2821 PetscErrorCode DMDestroy_Plex(DM dm)
2822 {
2823   DM_Plex *mesh = (DM_Plex *)dm->data;
2824 
2825   PetscFunctionBegin;
2826   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2827   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2828   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2829   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBounds_C", NULL));
2830   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2837   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2838   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2839   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2840   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2841   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2842   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2843   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2844   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2845   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2846   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2847   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2848   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2849   PetscCall(PetscFree(mesh->cones));
2850   PetscCall(PetscFree(mesh->coneOrientations));
2851   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2852   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2853   PetscCall(PetscFree(mesh->supports));
2854   PetscCall(PetscFree(mesh->cellTypes));
2855   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2856   PetscCall(PetscFree(mesh->tetgenOpts));
2857   PetscCall(PetscFree(mesh->triangleOpts));
2858   PetscCall(PetscFree(mesh->transformType));
2859   PetscCall(PetscFree(mesh->distributionName));
2860   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2861   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2862   PetscCall(ISDestroy(&mesh->subpointIS));
2863   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2864   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2865   if (mesh->periodic.face_sfs) {
2866     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2867     PetscCall(PetscFree(mesh->periodic.face_sfs));
2868   }
2869   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2870   if (mesh->periodic.periodic_points) {
2871     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2872     PetscCall(PetscFree(mesh->periodic.periodic_points));
2873   }
2874   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2875   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2876   PetscCall(ISDestroy(&mesh->anchorIS));
2877   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2878   PetscCall(PetscFree(mesh->parents));
2879   PetscCall(PetscFree(mesh->childIDs));
2880   PetscCall(PetscSectionDestroy(&mesh->childSection));
2881   PetscCall(PetscFree(mesh->children));
2882   PetscCall(DMDestroy(&mesh->referenceTree));
2883   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2884   PetscCall(PetscFree(mesh->neighbors));
2885   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2886   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2887   PetscCall(DMPlexTransformDestroy(&mesh->transform));
2888   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2889   PetscCall(PetscFree(mesh));
2890   PetscFunctionReturn(PETSC_SUCCESS);
2891 }
2892 
2893 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2894 {
2895   PetscSection           sectionGlobal, sectionLocal;
2896   PetscInt               bs = -1, mbs;
2897   PetscInt               localSize, localStart = 0;
2898   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2899   MatType                mtype;
2900   ISLocalToGlobalMapping ltog;
2901 
2902   PetscFunctionBegin;
2903   PetscCall(MatInitializePackage());
2904   mtype = dm->mattype;
2905   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2906   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2907   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2908   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2909   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2910   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2911   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2912   PetscCall(MatSetType(*J, mtype));
2913   PetscCall(MatSetFromOptions(*J));
2914   PetscCall(MatGetBlockSize(*J, &mbs));
2915   if (mbs > 1) bs = mbs;
2916   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2917   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2918   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2919   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2920   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2921   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2922   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2923   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2924   if (!isShell) {
2925     // There are three states with pblocks, since block starts can have no dofs:
2926     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2927     // TRUE)    Block Start: The first entry in a block has been added
2928     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2929     PetscBT         blst;
2930     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2931     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2932     const PetscInt *perm       = NULL;
2933     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2934     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2935 
2936     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2937     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2938     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2939 
2940     PetscCall(PetscCalloc1(localSize, &pblocks));
2941     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2942     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2943     // We need to process in the permuted order to get block sizes right
2944     for (PetscInt point = pStart; point < pEnd; ++point) {
2945       const PetscInt p = perm ? perm[point] : point;
2946 
2947       switch (dm->blocking_type) {
2948       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2949         PetscInt bdof, offset;
2950 
2951         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2952         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2953         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2954         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2955         if (dof > 0) {
2956           // State change
2957           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2958           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2959 
2960           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2961           // Signal block concatenation
2962           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2963         }
2964         dof  = dof < 0 ? -(dof + 1) : dof;
2965         bdof = cdof && (dof - cdof) ? 1 : dof;
2966         if (dof) {
2967           if (bs < 0) {
2968             bs = bdof;
2969           } else if (bs != bdof) {
2970             bs = 1;
2971           }
2972         }
2973       } break;
2974       case DM_BLOCKING_FIELD_NODE: {
2975         for (PetscInt field = 0; field < num_fields; field++) {
2976           PetscInt num_comp, bdof, offset;
2977           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2978           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2979           if (dof < 0) continue;
2980           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2981           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2982           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);
2983           PetscInt num_nodes = dof / num_comp;
2984           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2985           // Handle possibly constant block size (unlikely)
2986           bdof = cdof && (dof - cdof) ? 1 : dof;
2987           if (dof) {
2988             if (bs < 0) {
2989               bs = bdof;
2990             } else if (bs != bdof) {
2991               bs = 1;
2992             }
2993           }
2994         }
2995       } break;
2996       }
2997     }
2998     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2999     /* Must have same blocksize on all procs (some might have no points) */
3000     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
3001     bsLocal[1] = bs;
3002     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
3003     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
3004     else bs = bsMinMax[0];
3005     bs = PetscMax(1, bs);
3006     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
3007     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
3008       PetscCall(MatSetBlockSize(*J, bs));
3009       PetscCall(MatSetUp(*J));
3010     } else {
3011       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3012       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3013       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3014     }
3015     if (pblocks) { // Consolidate blocks
3016       PetscInt nblocks = 0;
3017       pblocks[0]       = PetscAbs(pblocks[0]);
3018       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3019         if (pblocks[i] == 0) continue;
3020         // Negative block size indicates the blocks should be concatenated
3021         if (pblocks[i] < 0) {
3022           pblocks[i] = -pblocks[i];
3023           pblocks[nblocks - 1] += pblocks[i];
3024         } else {
3025           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3026         }
3027         for (PetscInt j = 1; j < pblocks[i]; j++)
3028           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);
3029       }
3030       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3031     }
3032     PetscCall(PetscFree(pblocks));
3033   }
3034   PetscCall(MatSetDM(*J, dm));
3035   PetscFunctionReturn(PETSC_SUCCESS);
3036 }
3037 
3038 /*@
3039   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3040 
3041   Not Collective
3042 
3043   Input Parameter:
3044 . dm - The `DMPLEX`
3045 
3046   Output Parameter:
3047 . subsection - The subdomain section
3048 
3049   Level: developer
3050 
3051 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3052 @*/
3053 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3054 {
3055   DM_Plex *mesh = (DM_Plex *)dm->data;
3056 
3057   PetscFunctionBegin;
3058   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3059   if (!mesh->subdomainSection) {
3060     PetscSection section;
3061     PetscSF      sf;
3062 
3063     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3064     PetscCall(DMGetLocalSection(dm, &section));
3065     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3066     PetscCall(PetscSFDestroy(&sf));
3067   }
3068   *subsection = mesh->subdomainSection;
3069   PetscFunctionReturn(PETSC_SUCCESS);
3070 }
3071 
3072 /*@
3073   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3074 
3075   Not Collective
3076 
3077   Input Parameter:
3078 . dm - The `DMPLEX`
3079 
3080   Output Parameters:
3081 + pStart - The first mesh point
3082 - pEnd   - The upper bound for mesh points
3083 
3084   Level: beginner
3085 
3086 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3087 @*/
3088 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3089 {
3090   DM_Plex *mesh = (DM_Plex *)dm->data;
3091 
3092   PetscFunctionBegin;
3093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3094   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3095   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3096   PetscFunctionReturn(PETSC_SUCCESS);
3097 }
3098 
3099 /*@
3100   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3101 
3102   Not Collective
3103 
3104   Input Parameters:
3105 + dm     - The `DMPLEX`
3106 . pStart - The first mesh point
3107 - pEnd   - The upper bound for mesh points
3108 
3109   Level: beginner
3110 
3111 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3112 @*/
3113 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3114 {
3115   DM_Plex *mesh = (DM_Plex *)dm->data;
3116 
3117   PetscFunctionBegin;
3118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3119   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3120   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3121   PetscCall(PetscFree(mesh->cellTypes));
3122   PetscFunctionReturn(PETSC_SUCCESS);
3123 }
3124 
3125 /*@
3126   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3127 
3128   Not Collective
3129 
3130   Input Parameters:
3131 + dm - The `DMPLEX`
3132 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3133 
3134   Output Parameter:
3135 . size - The cone size for point `p`
3136 
3137   Level: beginner
3138 
3139 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3140 @*/
3141 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3142 {
3143   DM_Plex *mesh = (DM_Plex *)dm->data;
3144 
3145   PetscFunctionBegin;
3146   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3147   PetscAssertPointer(size, 3);
3148   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3149   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3150   PetscFunctionReturn(PETSC_SUCCESS);
3151 }
3152 
3153 /*@
3154   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3155 
3156   Not Collective
3157 
3158   Input Parameters:
3159 + dm   - The `DMPLEX`
3160 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3161 - size - The cone size for point `p`
3162 
3163   Level: beginner
3164 
3165   Note:
3166   This should be called after `DMPlexSetChart()`.
3167 
3168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3169 @*/
3170 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3171 {
3172   DM_Plex *mesh = (DM_Plex *)dm->data;
3173 
3174   PetscFunctionBegin;
3175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3176   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3177   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3178   PetscFunctionReturn(PETSC_SUCCESS);
3179 }
3180 
3181 /*@C
3182   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3183 
3184   Not Collective
3185 
3186   Input Parameters:
3187 + dm - The `DMPLEX`
3188 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3189 
3190   Output Parameter:
3191 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3192 
3193   Level: beginner
3194 
3195   Fortran Notes:
3196   `cone` must be declared with
3197 .vb
3198   PetscInt, pointer :: cone(:)
3199 .ve
3200 
3201   You must call `DMPlexRestoreCone()` after you finish using the array.
3202   `DMPlexRestoreCone()` is not needed/available in C.
3203 
3204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3205 @*/
3206 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3207 {
3208   DM_Plex *mesh = (DM_Plex *)dm->data;
3209   PetscInt off;
3210 
3211   PetscFunctionBegin;
3212   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3213   PetscAssertPointer(cone, 3);
3214   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3215   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3216   PetscFunctionReturn(PETSC_SUCCESS);
3217 }
3218 
3219 /*@
3220   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3221 
3222   Not Collective
3223 
3224   Input Parameters:
3225 + dm - The `DMPLEX`
3226 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3227 
3228   Output Parameters:
3229 + pConesSection - `PetscSection` describing the layout of `pCones`
3230 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3231 
3232   Level: intermediate
3233 
3234 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3235 @*/
3236 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3237 {
3238   PetscSection cs, newcs;
3239   PetscInt    *cones;
3240   PetscInt    *newarr = NULL;
3241   PetscInt     n;
3242 
3243   PetscFunctionBegin;
3244   PetscCall(DMPlexGetCones(dm, &cones));
3245   PetscCall(DMPlexGetConeSection(dm, &cs));
3246   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3247   if (pConesSection) *pConesSection = newcs;
3248   if (pCones) {
3249     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3250     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3251   }
3252   PetscFunctionReturn(PETSC_SUCCESS);
3253 }
3254 
3255 /*@
3256   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3257 
3258   Not Collective
3259 
3260   Input Parameters:
3261 + dm     - The `DMPLEX`
3262 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3263 
3264   Output Parameter:
3265 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3266 
3267   Level: advanced
3268 
3269   Notes:
3270   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3271 
3272   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3273 
3274 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3275           `DMPlexGetDepth()`, `IS`
3276 @*/
3277 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3278 {
3279   IS      *expandedPointsAll;
3280   PetscInt depth;
3281 
3282   PetscFunctionBegin;
3283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3284   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3285   PetscAssertPointer(expandedPoints, 3);
3286   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3287   *expandedPoints = expandedPointsAll[0];
3288   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3289   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3290   PetscFunctionReturn(PETSC_SUCCESS);
3291 }
3292 
3293 /*@
3294   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3295   (DAG points of depth 0, i.e., without cones).
3296 
3297   Not Collective
3298 
3299   Input Parameters:
3300 + dm     - The `DMPLEX`
3301 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3302 
3303   Output Parameters:
3304 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3305 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3306 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3307 
3308   Level: advanced
3309 
3310   Notes:
3311   Like `DMPlexGetConeTuple()` but recursive.
3312 
3313   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.
3314   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3315 
3316   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\:
3317   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3318   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3319 
3320 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3321           `DMPlexGetDepth()`, `PetscSection`, `IS`
3322 @*/
3323 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3324 {
3325   const PetscInt *arr0 = NULL, *cone = NULL;
3326   PetscInt       *arr = NULL, *newarr = NULL;
3327   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3328   IS             *expandedPoints_;
3329   PetscSection   *sections_;
3330 
3331   PetscFunctionBegin;
3332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3333   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3334   if (depth) PetscAssertPointer(depth, 3);
3335   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3336   if (sections) PetscAssertPointer(sections, 5);
3337   PetscCall(ISGetLocalSize(points, &n));
3338   PetscCall(ISGetIndices(points, &arr0));
3339   PetscCall(DMPlexGetDepth(dm, &depth_));
3340   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3341   PetscCall(PetscCalloc1(depth_, &sections_));
3342   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3343   for (d = depth_ - 1; d >= 0; d--) {
3344     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3345     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3346     for (i = 0; i < n; i++) {
3347       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3348       if (arr[i] >= start && arr[i] < end) {
3349         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3350         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3351       } else {
3352         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3353       }
3354     }
3355     PetscCall(PetscSectionSetUp(sections_[d]));
3356     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3357     PetscCall(PetscMalloc1(newn, &newarr));
3358     for (i = 0; i < n; i++) {
3359       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3360       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3361       if (cn > 1) {
3362         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3363         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3364       } else {
3365         newarr[co] = arr[i];
3366       }
3367     }
3368     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3369     arr = newarr;
3370     n   = newn;
3371   }
3372   PetscCall(ISRestoreIndices(points, &arr0));
3373   *depth = depth_;
3374   if (expandedPoints) *expandedPoints = expandedPoints_;
3375   else {
3376     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3377     PetscCall(PetscFree(expandedPoints_));
3378   }
3379   if (sections) *sections = sections_;
3380   else {
3381     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3382     PetscCall(PetscFree(sections_));
3383   }
3384   PetscFunctionReturn(PETSC_SUCCESS);
3385 }
3386 
3387 /*@
3388   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3389 
3390   Not Collective
3391 
3392   Input Parameters:
3393 + dm     - The `DMPLEX`
3394 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3395 
3396   Output Parameters:
3397 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3398 . expandedPoints - (optional) An array of recursively expanded cones
3399 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3400 
3401   Level: advanced
3402 
3403   Note:
3404   See `DMPlexGetConeRecursive()`
3405 
3406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3407           `DMPlexGetDepth()`, `IS`, `PetscSection`
3408 @*/
3409 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3410 {
3411   PetscInt d, depth_;
3412 
3413   PetscFunctionBegin;
3414   PetscCall(DMPlexGetDepth(dm, &depth_));
3415   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3416   if (depth) *depth = 0;
3417   if (expandedPoints) {
3418     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3419     PetscCall(PetscFree(*expandedPoints));
3420   }
3421   if (sections) {
3422     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3423     PetscCall(PetscFree(*sections));
3424   }
3425   PetscFunctionReturn(PETSC_SUCCESS);
3426 }
3427 
3428 /*@
3429   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
3430 
3431   Not Collective
3432 
3433   Input Parameters:
3434 + dm   - The `DMPLEX`
3435 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3436 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3437 
3438   Level: beginner
3439 
3440   Note:
3441   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3442 
3443 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3444 @*/
3445 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3446 {
3447   DM_Plex *mesh = (DM_Plex *)dm->data;
3448   PetscInt dof, off, c;
3449 
3450   PetscFunctionBegin;
3451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3452   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3453   if (dof) PetscAssertPointer(cone, 3);
3454   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3455   if (PetscDefined(USE_DEBUG)) {
3456     PetscInt pStart, pEnd;
3457     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3458     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);
3459     for (c = 0; c < dof; ++c) {
3460       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);
3461       mesh->cones[off + c] = cone[c];
3462     }
3463   } else {
3464     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3465   }
3466   PetscFunctionReturn(PETSC_SUCCESS);
3467 }
3468 
3469 /*@C
3470   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3471 
3472   Not Collective
3473 
3474   Input Parameters:
3475 + dm - The `DMPLEX`
3476 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3477 
3478   Output Parameter:
3479 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3480                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3481 
3482   Level: beginner
3483 
3484   Note:
3485   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3486   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3487   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3488   with the identity.
3489 
3490   Fortran Notes:
3491   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3492   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3493 
3494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3495           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3496 @*/
3497 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3498 {
3499   DM_Plex *mesh = (DM_Plex *)dm->data;
3500   PetscInt off;
3501 
3502   PetscFunctionBegin;
3503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3504   if (PetscDefined(USE_DEBUG)) {
3505     PetscInt dof;
3506     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3507     if (dof) PetscAssertPointer(coneOrientation, 3);
3508   }
3509   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3510 
3511   *coneOrientation = &mesh->coneOrientations[off];
3512   PetscFunctionReturn(PETSC_SUCCESS);
3513 }
3514 
3515 /*@
3516   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3517 
3518   Not Collective
3519 
3520   Input Parameters:
3521 + dm              - The `DMPLEX`
3522 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3523 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3524 
3525   Level: beginner
3526 
3527   Notes:
3528   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3529 
3530   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3531 
3532 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3533 @*/
3534 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3535 {
3536   DM_Plex *mesh = (DM_Plex *)dm->data;
3537   PetscInt pStart, pEnd;
3538   PetscInt dof, off, c;
3539 
3540   PetscFunctionBegin;
3541   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3542   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3543   if (dof) PetscAssertPointer(coneOrientation, 3);
3544   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3545   if (PetscDefined(USE_DEBUG)) {
3546     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3547     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);
3548     for (c = 0; c < dof; ++c) {
3549       PetscInt cdof, o = coneOrientation[c];
3550 
3551       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3552       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);
3553       mesh->coneOrientations[off + c] = o;
3554     }
3555   } else {
3556     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3557   }
3558   PetscFunctionReturn(PETSC_SUCCESS);
3559 }
3560 
3561 /*@
3562   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3563 
3564   Not Collective
3565 
3566   Input Parameters:
3567 + dm        - The `DMPLEX`
3568 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3569 . conePos   - The local index in the cone where the point should be put
3570 - conePoint - The mesh point to insert
3571 
3572   Level: beginner
3573 
3574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3575 @*/
3576 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3577 {
3578   DM_Plex *mesh = (DM_Plex *)dm->data;
3579   PetscInt pStart, pEnd;
3580   PetscInt dof, off;
3581 
3582   PetscFunctionBegin;
3583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3584   if (PetscDefined(USE_DEBUG)) {
3585     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3586     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);
3587     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);
3588     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3589     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);
3590   }
3591   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3592   mesh->cones[off + conePos] = conePoint;
3593   PetscFunctionReturn(PETSC_SUCCESS);
3594 }
3595 
3596 /*@
3597   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3598 
3599   Not Collective
3600 
3601   Input Parameters:
3602 + dm              - The `DMPLEX`
3603 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3604 . conePos         - The local index in the cone where the point should be put
3605 - coneOrientation - The point orientation to insert
3606 
3607   Level: beginner
3608 
3609   Note:
3610   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3611 
3612 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3613 @*/
3614 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3615 {
3616   DM_Plex *mesh = (DM_Plex *)dm->data;
3617   PetscInt pStart, pEnd;
3618   PetscInt dof, off;
3619 
3620   PetscFunctionBegin;
3621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3622   if (PetscDefined(USE_DEBUG)) {
3623     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3624     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);
3625     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3626     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);
3627   }
3628   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3629   mesh->coneOrientations[off + conePos] = coneOrientation;
3630   PetscFunctionReturn(PETSC_SUCCESS);
3631 }
3632 
3633 /*@C
3634   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3635 
3636   Not collective
3637 
3638   Input Parameters:
3639 + dm - The DMPlex
3640 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3641 
3642   Output Parameters:
3643 + cone - An array of points which are on the in-edges for point `p`
3644 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3645          integer giving the prescription for cone traversal.
3646 
3647   Level: beginner
3648 
3649   Notes:
3650   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3651   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3652   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3653   with the identity.
3654 
3655   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3656 
3657   Fortran Notes:
3658   `cone` and `ornt` must be declared with
3659 .vb
3660   PetscInt, pointer :: cone(:)
3661   PetscInt, pointer :: ornt(:)
3662 .ve
3663 
3664 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3665 @*/
3666 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3667 {
3668   DM_Plex *mesh = (DM_Plex *)dm->data;
3669 
3670   PetscFunctionBegin;
3671   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3672   if (mesh->tr) {
3673     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3674   } else {
3675     PetscInt off;
3676     if (PetscDefined(USE_DEBUG)) {
3677       PetscInt dof;
3678       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3679       if (dof) {
3680         if (cone) PetscAssertPointer(cone, 3);
3681         if (ornt) PetscAssertPointer(ornt, 4);
3682       }
3683     }
3684     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3685     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3686     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3687   }
3688   PetscFunctionReturn(PETSC_SUCCESS);
3689 }
3690 
3691 /*@C
3692   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3693 
3694   Not Collective
3695 
3696   Input Parameters:
3697 + dm   - The DMPlex
3698 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3699 . cone - An array of points which are on the in-edges for point p
3700 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3701          integer giving the prescription for cone traversal.
3702 
3703   Level: beginner
3704 
3705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3706 @*/
3707 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3708 {
3709   DM_Plex *mesh = (DM_Plex *)dm->data;
3710 
3711   PetscFunctionBegin;
3712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3713   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3714   PetscFunctionReturn(PETSC_SUCCESS);
3715 }
3716 
3717 /*@
3718   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3719 
3720   Not Collective
3721 
3722   Input Parameters:
3723 + dm - The `DMPLEX`
3724 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3725 
3726   Output Parameter:
3727 . size - The support size for point `p`
3728 
3729   Level: beginner
3730 
3731 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3732 @*/
3733 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3734 {
3735   DM_Plex *mesh = (DM_Plex *)dm->data;
3736 
3737   PetscFunctionBegin;
3738   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3739   PetscAssertPointer(size, 3);
3740   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3741   PetscFunctionReturn(PETSC_SUCCESS);
3742 }
3743 
3744 /*@
3745   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3746 
3747   Not Collective
3748 
3749   Input Parameters:
3750 + dm   - The `DMPLEX`
3751 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3752 - size - The support size for point `p`
3753 
3754   Level: beginner
3755 
3756   Note:
3757   This should be called after `DMPlexSetChart()`.
3758 
3759 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3760 @*/
3761 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3762 {
3763   DM_Plex *mesh = (DM_Plex *)dm->data;
3764 
3765   PetscFunctionBegin;
3766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3767   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3768   PetscFunctionReturn(PETSC_SUCCESS);
3769 }
3770 
3771 /*@C
3772   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3773 
3774   Not Collective
3775 
3776   Input Parameters:
3777 + dm - The `DMPLEX`
3778 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3779 
3780   Output Parameter:
3781 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3782 
3783   Level: beginner
3784 
3785   Fortran Notes:
3786   `support` must be declared with
3787 .vb
3788   PetscInt, pointer :: support(:)
3789 .ve
3790 
3791   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3792   `DMPlexRestoreSupport()` is not needed/available in C.
3793 
3794 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3795 @*/
3796 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3797 {
3798   DM_Plex *mesh = (DM_Plex *)dm->data;
3799   PetscInt off;
3800 
3801   PetscFunctionBegin;
3802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3803   PetscAssertPointer(support, 3);
3804   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3805   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3806   PetscFunctionReturn(PETSC_SUCCESS);
3807 }
3808 
3809 /*@
3810   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3811 
3812   Not Collective
3813 
3814   Input Parameters:
3815 + dm      - The `DMPLEX`
3816 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3817 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3818 
3819   Level: beginner
3820 
3821   Note:
3822   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3823 
3824 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3825 @*/
3826 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3827 {
3828   DM_Plex *mesh = (DM_Plex *)dm->data;
3829   PetscInt pStart, pEnd;
3830   PetscInt dof, off, c;
3831 
3832   PetscFunctionBegin;
3833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3834   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3835   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3836   if (dof) PetscAssertPointer(support, 3);
3837   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3838   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);
3839   for (c = 0; c < dof; ++c) {
3840     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);
3841     mesh->supports[off + c] = support[c];
3842   }
3843   PetscFunctionReturn(PETSC_SUCCESS);
3844 }
3845 
3846 /*@
3847   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3848 
3849   Not Collective
3850 
3851   Input Parameters:
3852 + dm           - The `DMPLEX`
3853 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3854 . supportPos   - The local index in the cone where the point should be put
3855 - supportPoint - The mesh point to insert
3856 
3857   Level: beginner
3858 
3859 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3860 @*/
3861 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3862 {
3863   DM_Plex *mesh = (DM_Plex *)dm->data;
3864   PetscInt pStart, pEnd;
3865   PetscInt dof, off;
3866 
3867   PetscFunctionBegin;
3868   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3869   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3870   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3871   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3872   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);
3873   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);
3874   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);
3875   mesh->supports[off + supportPos] = supportPoint;
3876   PetscFunctionReturn(PETSC_SUCCESS);
3877 }
3878 
3879 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3880 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3881 {
3882   switch (ct) {
3883   case DM_POLYTOPE_SEGMENT:
3884     if (o == -1) return -2;
3885     break;
3886   case DM_POLYTOPE_TRIANGLE:
3887     if (o == -3) return -1;
3888     if (o == -2) return -3;
3889     if (o == -1) return -2;
3890     break;
3891   case DM_POLYTOPE_QUADRILATERAL:
3892     if (o == -4) return -2;
3893     if (o == -3) return -1;
3894     if (o == -2) return -4;
3895     if (o == -1) return -3;
3896     break;
3897   default:
3898     return o;
3899   }
3900   return o;
3901 }
3902 
3903 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3904 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3905 {
3906   switch (ct) {
3907   case DM_POLYTOPE_SEGMENT:
3908     if ((o == -2) || (o == 1)) return -1;
3909     if (o == -1) return 0;
3910     break;
3911   case DM_POLYTOPE_TRIANGLE:
3912     if (o == -3) return -2;
3913     if (o == -2) return -1;
3914     if (o == -1) return -3;
3915     break;
3916   case DM_POLYTOPE_QUADRILATERAL:
3917     if (o == -4) return -2;
3918     if (o == -3) return -1;
3919     if (o == -2) return -4;
3920     if (o == -1) return -3;
3921     break;
3922   default:
3923     return o;
3924   }
3925   return o;
3926 }
3927 
3928 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3929 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3930 {
3931   PetscInt pStart, pEnd, p;
3932 
3933   PetscFunctionBegin;
3934   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3935   for (p = pStart; p < pEnd; ++p) {
3936     const PetscInt *cone, *ornt;
3937     PetscInt        coneSize, c;
3938 
3939     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3940     PetscCall(DMPlexGetCone(dm, p, &cone));
3941     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3942     for (c = 0; c < coneSize; ++c) {
3943       DMPolytopeType ct;
3944       const PetscInt o = ornt[c];
3945 
3946       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3947       switch (ct) {
3948       case DM_POLYTOPE_SEGMENT:
3949         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3950         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3951         break;
3952       case DM_POLYTOPE_TRIANGLE:
3953         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3954         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3955         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3956         break;
3957       case DM_POLYTOPE_QUADRILATERAL:
3958         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3959         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3960         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3961         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3962         break;
3963       default:
3964         break;
3965       }
3966     }
3967   }
3968   PetscFunctionReturn(PETSC_SUCCESS);
3969 }
3970 
3971 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3972 {
3973   DM_Plex *mesh = (DM_Plex *)dm->data;
3974 
3975   PetscFunctionBeginHot;
3976   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3977     if (useCone) {
3978       PetscCall(DMPlexGetConeSize(dm, p, size));
3979       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3980     } else {
3981       PetscCall(DMPlexGetSupportSize(dm, p, size));
3982       PetscCall(DMPlexGetSupport(dm, p, arr));
3983     }
3984   } else {
3985     if (useCone) {
3986       const PetscSection s   = mesh->coneSection;
3987       const PetscInt     ps  = p - s->pStart;
3988       const PetscInt     off = s->atlasOff[ps];
3989 
3990       *size = s->atlasDof[ps];
3991       *arr  = mesh->cones + off;
3992       *ornt = mesh->coneOrientations + off;
3993     } else {
3994       const PetscSection s   = mesh->supportSection;
3995       const PetscInt     ps  = p - s->pStart;
3996       const PetscInt     off = s->atlasOff[ps];
3997 
3998       *size = s->atlasDof[ps];
3999       *arr  = mesh->supports + off;
4000     }
4001   }
4002   PetscFunctionReturn(PETSC_SUCCESS);
4003 }
4004 
4005 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
4006 {
4007   DM_Plex *mesh = (DM_Plex *)dm->data;
4008 
4009   PetscFunctionBeginHot;
4010   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4011     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4012   }
4013   PetscFunctionReturn(PETSC_SUCCESS);
4014 }
4015 
4016 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4017 {
4018   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4019   PetscInt       *closure;
4020   const PetscInt *tmp = NULL, *tmpO = NULL;
4021   PetscInt        off = 0, tmpSize, t;
4022 
4023   PetscFunctionBeginHot;
4024   if (ornt) {
4025     PetscCall(DMPlexGetCellType(dm, p, &ct));
4026     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;
4027   }
4028   if (*points) {
4029     closure = *points;
4030   } else {
4031     PetscInt maxConeSize, maxSupportSize;
4032     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4033     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4034   }
4035   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4036   if (ct == DM_POLYTOPE_UNKNOWN) {
4037     closure[off++] = p;
4038     closure[off++] = 0;
4039     for (t = 0; t < tmpSize; ++t) {
4040       closure[off++] = tmp[t];
4041       closure[off++] = tmpO ? tmpO[t] : 0;
4042     }
4043   } else {
4044     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4045 
4046     /* We assume that cells with a valid type have faces with a valid type */
4047     closure[off++] = p;
4048     closure[off++] = ornt;
4049     for (t = 0; t < tmpSize; ++t) {
4050       DMPolytopeType ft;
4051 
4052       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4053       closure[off++] = tmp[arr[t]];
4054       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4055     }
4056   }
4057   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4058   if (numPoints) *numPoints = tmpSize + 1;
4059   if (points) *points = closure;
4060   PetscFunctionReturn(PETSC_SUCCESS);
4061 }
4062 
4063 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4064 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4065 {
4066   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4067   const PetscInt *cone, *ornt;
4068   PetscInt       *pts, *closure = NULL;
4069   DMPolytopeType  ft;
4070   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4071   PetscInt        dim, coneSize, c, d, clSize, cl;
4072 
4073   PetscFunctionBeginHot;
4074   PetscCall(DMGetDimension(dm, &dim));
4075   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4076   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4077   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4078   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4079   maxSize       = PetscMax(coneSeries, supportSeries);
4080   if (*points) {
4081     pts = *points;
4082   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4083   c        = 0;
4084   pts[c++] = point;
4085   pts[c++] = o;
4086   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4087   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4088   for (cl = 0; cl < clSize * 2; cl += 2) {
4089     pts[c++] = closure[cl];
4090     pts[c++] = closure[cl + 1];
4091   }
4092   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), 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(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4098   for (d = 2; d < coneSize; ++d) {
4099     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4100     pts[c++] = cone[arr[d * 2 + 0]];
4101     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4102   }
4103   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4104   if (dim >= 3) {
4105     for (d = 2; d < coneSize; ++d) {
4106       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4107       const PetscInt *fcone, *fornt;
4108       PetscInt        fconeSize, fc, i;
4109 
4110       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4111       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4112       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4113       for (fc = 0; fc < fconeSize; ++fc) {
4114         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4115         const PetscInt co = farr[fc * 2 + 1];
4116 
4117         for (i = 0; i < c; i += 2)
4118           if (pts[i] == cp) break;
4119         if (i == c) {
4120           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4121           pts[c++] = cp;
4122           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4123         }
4124       }
4125       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4126     }
4127   }
4128   *numPoints = c / 2;
4129   *points    = pts;
4130   PetscFunctionReturn(PETSC_SUCCESS);
4131 }
4132 
4133 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4134 {
4135   DMPolytopeType ct;
4136   PetscInt      *closure, *fifo;
4137   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4138   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4139   PetscInt       depth, maxSize;
4140 
4141   PetscFunctionBeginHot;
4142   PetscCall(DMPlexGetDepth(dm, &depth));
4143   if (depth == 1) {
4144     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4145     PetscFunctionReturn(PETSC_SUCCESS);
4146   }
4147   PetscCall(DMPlexGetCellType(dm, p, &ct));
4148   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;
4149   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4150     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4151     PetscFunctionReturn(PETSC_SUCCESS);
4152   }
4153   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4154   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4155   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4156   maxSize       = PetscMax(coneSeries, supportSeries);
4157   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4158   if (*points) {
4159     closure = *points;
4160   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4161   closure[closureSize++] = p;
4162   closure[closureSize++] = ornt;
4163   fifo[fifoSize++]       = p;
4164   fifo[fifoSize++]       = ornt;
4165   fifo[fifoSize++]       = ct;
4166   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4167   while (fifoSize - fifoStart) {
4168     const PetscInt       q    = fifo[fifoStart++];
4169     const PetscInt       o    = fifo[fifoStart++];
4170     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4171     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4172     const PetscInt      *tmp, *tmpO = NULL;
4173     PetscInt             tmpSize, t;
4174 
4175     if (PetscDefined(USE_DEBUG)) {
4176       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4177       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);
4178     }
4179     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4180     for (t = 0; t < tmpSize; ++t) {
4181       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4182       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4183       const PetscInt cp = tmp[ip];
4184       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4185       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4186       PetscInt       c;
4187 
4188       /* Check for duplicate */
4189       for (c = 0; c < closureSize; c += 2) {
4190         if (closure[c] == cp) break;
4191       }
4192       if (c == closureSize) {
4193         closure[closureSize++] = cp;
4194         closure[closureSize++] = co;
4195         fifo[fifoSize++]       = cp;
4196         fifo[fifoSize++]       = co;
4197         fifo[fifoSize++]       = ct;
4198       }
4199     }
4200     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4201   }
4202   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4203   if (numPoints) *numPoints = closureSize / 2;
4204   if (points) *points = closure;
4205   PetscFunctionReturn(PETSC_SUCCESS);
4206 }
4207 
4208 /*@C
4209   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4210 
4211   Not Collective
4212 
4213   Input Parameters:
4214 + dm      - The `DMPLEX`
4215 . p       - The mesh point
4216 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4217 
4218   Input/Output Parameter:
4219 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4220            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4221            otherwise the provided array is used to hold the values
4222 
4223   Output Parameter:
4224 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4225 
4226   Level: beginner
4227 
4228   Note:
4229   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4230 
4231   Fortran Notes:
4232   `points` must be declared with
4233 .vb
4234   PetscInt, pointer :: points(:)
4235 .ve
4236   and is always allocated by the function.
4237 
4238   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4239 
4240 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4241 @*/
4242 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4243 {
4244   PetscFunctionBeginHot;
4245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4246   if (numPoints) PetscAssertPointer(numPoints, 4);
4247   if (points) PetscAssertPointer(points, 5);
4248   if (PetscDefined(USE_DEBUG)) {
4249     PetscInt pStart, pEnd;
4250     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4251     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);
4252   }
4253   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4254   PetscFunctionReturn(PETSC_SUCCESS);
4255 }
4256 
4257 /*@C
4258   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4259 
4260   Not Collective
4261 
4262   Input Parameters:
4263 + dm        - The `DMPLEX`
4264 . p         - The mesh point
4265 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4266 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4267 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4268 
4269   Level: beginner
4270 
4271   Note:
4272   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4273 
4274 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4275 @*/
4276 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4277 {
4278   PetscFunctionBeginHot;
4279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4280   if (numPoints) *numPoints = 0;
4281   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4282   PetscFunctionReturn(PETSC_SUCCESS);
4283 }
4284 
4285 /*@
4286   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4287 
4288   Not Collective
4289 
4290   Input Parameter:
4291 . dm - The `DMPLEX`
4292 
4293   Output Parameters:
4294 + maxConeSize    - The maximum number of in-edges
4295 - maxSupportSize - The maximum number of out-edges
4296 
4297   Level: beginner
4298 
4299 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4300 @*/
4301 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4302 {
4303   DM_Plex *mesh = (DM_Plex *)dm->data;
4304 
4305   PetscFunctionBegin;
4306   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4307   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4308   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4309   PetscFunctionReturn(PETSC_SUCCESS);
4310 }
4311 
4312 PetscErrorCode DMSetUp_Plex(DM dm)
4313 {
4314   DM_Plex *mesh = (DM_Plex *)dm->data;
4315   PetscInt size, maxSupportSize;
4316 
4317   PetscFunctionBegin;
4318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4319   PetscCall(PetscSectionSetUp(mesh->coneSection));
4320   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4321   PetscCall(PetscMalloc1(size, &mesh->cones));
4322   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4323   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4324   if (maxSupportSize) {
4325     PetscCall(PetscSectionSetUp(mesh->supportSection));
4326     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4327     PetscCall(PetscMalloc1(size, &mesh->supports));
4328   }
4329   PetscFunctionReturn(PETSC_SUCCESS);
4330 }
4331 
4332 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4333 {
4334   PetscFunctionBegin;
4335   if (subdm) PetscCall(DMClone(dm, subdm));
4336   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4337   if (subdm) (*subdm)->useNatural = dm->useNatural;
4338   if (dm->useNatural && dm->sfMigration) {
4339     PetscSF sfNatural;
4340 
4341     (*subdm)->sfMigration = dm->sfMigration;
4342     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4343     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4344     (*subdm)->sfNatural = sfNatural;
4345   }
4346   PetscFunctionReturn(PETSC_SUCCESS);
4347 }
4348 
4349 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4350 {
4351   PetscInt i = 0;
4352 
4353   PetscFunctionBegin;
4354   PetscCall(DMClone(dms[0], superdm));
4355   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4356   (*superdm)->useNatural = PETSC_FALSE;
4357   for (i = 0; i < len; i++) {
4358     if (dms[i]->useNatural && dms[i]->sfMigration) {
4359       PetscSF sfNatural;
4360 
4361       (*superdm)->sfMigration = dms[i]->sfMigration;
4362       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4363       (*superdm)->useNatural = PETSC_TRUE;
4364       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4365       (*superdm)->sfNatural = sfNatural;
4366       break;
4367     }
4368   }
4369   PetscFunctionReturn(PETSC_SUCCESS);
4370 }
4371 
4372 /*@
4373   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4374 
4375   Not Collective
4376 
4377   Input Parameter:
4378 . dm - The `DMPLEX`
4379 
4380   Level: beginner
4381 
4382   Note:
4383   This should be called after all calls to `DMPlexSetCone()`
4384 
4385 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4386 @*/
4387 PetscErrorCode DMPlexSymmetrize(DM dm)
4388 {
4389   DM_Plex  *mesh = (DM_Plex *)dm->data;
4390   PetscInt *offsets;
4391   PetscInt  supportSize;
4392   PetscInt  pStart, pEnd, p;
4393 
4394   PetscFunctionBegin;
4395   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4396   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4397   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4398   /* Calculate support sizes */
4399   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4400   for (p = pStart; p < pEnd; ++p) {
4401     PetscInt dof, off, c;
4402 
4403     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4404     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4405     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4406   }
4407   PetscCall(PetscSectionSetUp(mesh->supportSection));
4408   /* Calculate supports */
4409   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4410   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4411   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4412   for (p = pStart; p < pEnd; ++p) {
4413     PetscInt dof, off, c;
4414 
4415     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4416     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4417     for (c = off; c < off + dof; ++c) {
4418       const PetscInt q = mesh->cones[c];
4419       PetscInt       offS;
4420 
4421       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4422 
4423       mesh->supports[offS + offsets[q]] = p;
4424       ++offsets[q];
4425     }
4426   }
4427   PetscCall(PetscFree(offsets));
4428   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4429   PetscFunctionReturn(PETSC_SUCCESS);
4430 }
4431 
4432 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4433 {
4434   IS stratumIS;
4435 
4436   PetscFunctionBegin;
4437   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4438   if (PetscDefined(USE_DEBUG)) {
4439     PetscInt  qStart, qEnd, numLevels, level;
4440     PetscBool overlap = PETSC_FALSE;
4441     PetscCall(DMLabelGetNumValues(label, &numLevels));
4442     for (level = 0; level < numLevels; level++) {
4443       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4444       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4445         overlap = PETSC_TRUE;
4446         break;
4447       }
4448     }
4449     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);
4450   }
4451   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4452   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4453   PetscCall(ISDestroy(&stratumIS));
4454   PetscFunctionReturn(PETSC_SUCCESS);
4455 }
4456 
4457 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4458 {
4459   PetscInt *pMin, *pMax;
4460   PetscInt  pStart, pEnd;
4461   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4462 
4463   PetscFunctionBegin;
4464   {
4465     DMLabel label2;
4466 
4467     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4468     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4469   }
4470   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4471   for (PetscInt p = pStart; p < pEnd; ++p) {
4472     DMPolytopeType ct;
4473 
4474     PetscCall(DMPlexGetCellType(dm, p, &ct));
4475     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4476     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4477   }
4478   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4479   for (PetscInt d = dmin; d <= dmax; ++d) {
4480     pMin[d] = PETSC_INT_MAX;
4481     pMax[d] = PETSC_INT_MIN;
4482   }
4483   for (PetscInt p = pStart; p < pEnd; ++p) {
4484     DMPolytopeType ct;
4485     PetscInt       d;
4486 
4487     PetscCall(DMPlexGetCellType(dm, p, &ct));
4488     d       = DMPolytopeTypeGetDim(ct);
4489     pMin[d] = PetscMin(p, pMin[d]);
4490     pMax[d] = PetscMax(p, pMax[d]);
4491   }
4492   for (PetscInt d = dmin; d <= dmax; ++d) {
4493     if (pMin[d] > pMax[d]) continue;
4494     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4495   }
4496   PetscCall(PetscFree2(pMin, pMax));
4497   PetscFunctionReturn(PETSC_SUCCESS);
4498 }
4499 
4500 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4501 {
4502   PetscInt pStart, pEnd;
4503   PetscInt numRoots = 0, numLeaves = 0;
4504 
4505   PetscFunctionBegin;
4506   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4507   {
4508     /* Initialize roots and count leaves */
4509     PetscInt sMin = PETSC_INT_MAX;
4510     PetscInt sMax = PETSC_INT_MIN;
4511     PetscInt coneSize, supportSize;
4512 
4513     for (PetscInt p = pStart; p < pEnd; ++p) {
4514       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4515       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4516       if (!coneSize && supportSize) {
4517         sMin = PetscMin(p, sMin);
4518         sMax = PetscMax(p, sMax);
4519         ++numRoots;
4520       } else if (!supportSize && coneSize) {
4521         ++numLeaves;
4522       } else if (!supportSize && !coneSize) {
4523         /* Isolated points */
4524         sMin = PetscMin(p, sMin);
4525         sMax = PetscMax(p, sMax);
4526       }
4527     }
4528     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4529   }
4530 
4531   if (numRoots + numLeaves == (pEnd - pStart)) {
4532     PetscInt sMin = PETSC_INT_MAX;
4533     PetscInt sMax = PETSC_INT_MIN;
4534     PetscInt coneSize, supportSize;
4535 
4536     for (PetscInt p = pStart; p < pEnd; ++p) {
4537       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4538       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4539       if (!supportSize && coneSize) {
4540         sMin = PetscMin(p, sMin);
4541         sMax = PetscMax(p, sMax);
4542       }
4543     }
4544     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4545   } else {
4546     PetscInt level = 0;
4547     PetscInt qStart, qEnd;
4548 
4549     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4550     while (qEnd > qStart) {
4551       PetscInt sMin = PETSC_INT_MAX;
4552       PetscInt sMax = PETSC_INT_MIN;
4553 
4554       for (PetscInt q = qStart; q < qEnd; ++q) {
4555         const PetscInt *support;
4556         PetscInt        supportSize;
4557 
4558         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4559         PetscCall(DMPlexGetSupport(dm, q, &support));
4560         for (PetscInt s = 0; s < supportSize; ++s) {
4561           sMin = PetscMin(support[s], sMin);
4562           sMax = PetscMax(support[s], sMax);
4563         }
4564       }
4565       PetscCall(DMLabelGetNumValues(label, &level));
4566       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4567       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4568     }
4569   }
4570   PetscFunctionReturn(PETSC_SUCCESS);
4571 }
4572 
4573 /*@
4574   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4575 
4576   Collective
4577 
4578   Input Parameter:
4579 . dm - The `DMPLEX`
4580 
4581   Level: beginner
4582 
4583   Notes:
4584   The strata group all points of the same grade, and this function calculates the strata. This
4585   grade can be seen as the height (or depth) of the point in the DAG.
4586 
4587   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4588   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4589   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4590   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4591   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4592   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4593   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4594 
4595   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4596   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4597   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
4598   to interpolate only that one (e0), so that
4599 .vb
4600   cone(c0) = {e0, v2}
4601   cone(e0) = {v0, v1}
4602 .ve
4603   If `DMPlexStratify()` is run on this mesh, it will give depths
4604 .vb
4605    depth 0 = {v0, v1, v2}
4606    depth 1 = {e0, c0}
4607 .ve
4608   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4609 
4610   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4611 
4612 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4613 @*/
4614 PetscErrorCode DMPlexStratify(DM dm)
4615 {
4616   DM_Plex  *mesh = (DM_Plex *)dm->data;
4617   DMLabel   label;
4618   PetscBool flg = PETSC_FALSE;
4619 
4620   PetscFunctionBegin;
4621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4622   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4623 
4624   // Create depth label
4625   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4626   PetscCall(DMCreateLabel(dm, "depth"));
4627   PetscCall(DMPlexGetDepthLabel(dm, &label));
4628 
4629   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4630   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4631   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4632 
4633   { /* just in case there is an empty process */
4634     PetscInt numValues, maxValues = 0, v;
4635 
4636     PetscCall(DMLabelGetNumValues(label, &numValues));
4637     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4638     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4639   }
4640   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4641   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4642   PetscFunctionReturn(PETSC_SUCCESS);
4643 }
4644 
4645 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4646 {
4647   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4648   PetscInt       dim, depth, pheight, coneSize;
4649   PetscBool      preferTensor;
4650 
4651   PetscFunctionBeginHot;
4652   PetscCall(DMGetDimension(dm, &dim));
4653   PetscCall(DMPlexGetDepth(dm, &depth));
4654   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4655   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4656   pheight = depth - pdepth;
4657   if (depth <= 1) {
4658     switch (pdepth) {
4659     case 0:
4660       ct = DM_POLYTOPE_POINT;
4661       break;
4662     case 1:
4663       switch (coneSize) {
4664       case 2:
4665         ct = DM_POLYTOPE_SEGMENT;
4666         break;
4667       case 3:
4668         ct = DM_POLYTOPE_TRIANGLE;
4669         break;
4670       case 4:
4671         switch (dim) {
4672         case 2:
4673           ct = DM_POLYTOPE_QUADRILATERAL;
4674           break;
4675         case 3:
4676           ct = DM_POLYTOPE_TETRAHEDRON;
4677           break;
4678         default:
4679           break;
4680         }
4681         break;
4682       case 5:
4683         ct = DM_POLYTOPE_PYRAMID;
4684         break;
4685       case 6:
4686         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4687         break;
4688       case 8:
4689         ct = DM_POLYTOPE_HEXAHEDRON;
4690         break;
4691       default:
4692         break;
4693       }
4694     }
4695   } else {
4696     if (pdepth == 0) {
4697       ct = DM_POLYTOPE_POINT;
4698     } else if (pheight == 0) {
4699       switch (dim) {
4700       case 1:
4701         switch (coneSize) {
4702         case 2:
4703           ct = DM_POLYTOPE_SEGMENT;
4704           break;
4705         default:
4706           break;
4707         }
4708         break;
4709       case 2:
4710         switch (coneSize) {
4711         case 3:
4712           ct = DM_POLYTOPE_TRIANGLE;
4713           break;
4714         case 4:
4715           ct = DM_POLYTOPE_QUADRILATERAL;
4716           break;
4717         default:
4718           break;
4719         }
4720         break;
4721       case 3:
4722         switch (coneSize) {
4723         case 4:
4724           ct = DM_POLYTOPE_TETRAHEDRON;
4725           break;
4726         case 5: {
4727           const PetscInt *cone;
4728           PetscInt        faceConeSize;
4729 
4730           PetscCall(DMPlexGetCone(dm, p, &cone));
4731           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4732           switch (faceConeSize) {
4733           case 3:
4734             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4735             break;
4736           case 4:
4737             ct = DM_POLYTOPE_PYRAMID;
4738             break;
4739           }
4740         } break;
4741         case 6:
4742           ct = DM_POLYTOPE_HEXAHEDRON;
4743           break;
4744         default:
4745           break;
4746         }
4747         break;
4748       default:
4749         break;
4750       }
4751     } else if (pheight > 0) {
4752       switch (coneSize) {
4753       case 2:
4754         ct = DM_POLYTOPE_SEGMENT;
4755         break;
4756       case 3:
4757         ct = DM_POLYTOPE_TRIANGLE;
4758         break;
4759       case 4:
4760         ct = DM_POLYTOPE_QUADRILATERAL;
4761         break;
4762       default:
4763         break;
4764       }
4765     }
4766   }
4767   *pt = ct;
4768   PetscFunctionReturn(PETSC_SUCCESS);
4769 }
4770 
4771 /*@
4772   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4773 
4774   Collective
4775 
4776   Input Parameter:
4777 . dm - The `DMPLEX`
4778 
4779   Level: developer
4780 
4781   Note:
4782   This function is normally called automatically when a cell type is requested. It creates an
4783   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4784   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4785 
4786   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4787 
4788 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4789 @*/
4790 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4791 {
4792   DM_Plex *mesh;
4793   DMLabel  ctLabel;
4794   PetscInt pStart, pEnd, p;
4795 
4796   PetscFunctionBegin;
4797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4798   mesh = (DM_Plex *)dm->data;
4799   PetscCall(DMCreateLabel(dm, "celltype"));
4800   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4801   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4802   PetscCall(PetscFree(mesh->cellTypes));
4803   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4804   for (p = pStart; p < pEnd; ++p) {
4805     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4806     PetscInt       pdepth;
4807 
4808     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4809     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4810     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]);
4811     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4812     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4813   }
4814   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4815   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4816   PetscFunctionReturn(PETSC_SUCCESS);
4817 }
4818 
4819 /*@C
4820   DMPlexGetJoin - Get an array for the join of the set of points
4821 
4822   Not Collective
4823 
4824   Input Parameters:
4825 + dm        - The `DMPLEX` object
4826 . numPoints - The number of input points for the join
4827 - points    - The input points
4828 
4829   Output Parameters:
4830 + numCoveredPoints - The number of points in the join
4831 - coveredPoints    - The points in the join
4832 
4833   Level: intermediate
4834 
4835   Note:
4836   Currently, this is restricted to a single level join
4837 
4838   Fortran Notes:
4839   `converedPoints` must be declared with
4840 .vb
4841   PetscInt, pointer :: coveredPints(:)
4842 .ve
4843 
4844 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4845 @*/
4846 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4847 {
4848   DM_Plex  *mesh = (DM_Plex *)dm->data;
4849   PetscInt *join[2];
4850   PetscInt  joinSize, i = 0;
4851   PetscInt  dof, off, p, c, m;
4852   PetscInt  maxSupportSize;
4853 
4854   PetscFunctionBegin;
4855   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4856   PetscAssertPointer(points, 3);
4857   PetscAssertPointer(numCoveredPoints, 4);
4858   PetscAssertPointer(coveredPoints, 5);
4859   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4860   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4861   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4862   /* Copy in support of first point */
4863   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4864   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4865   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4866   /* Check each successive support */
4867   for (p = 1; p < numPoints; ++p) {
4868     PetscInt newJoinSize = 0;
4869 
4870     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4871     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4872     for (c = 0; c < dof; ++c) {
4873       const PetscInt point = mesh->supports[off + c];
4874 
4875       for (m = 0; m < joinSize; ++m) {
4876         if (point == join[i][m]) {
4877           join[1 - i][newJoinSize++] = point;
4878           break;
4879         }
4880       }
4881     }
4882     joinSize = newJoinSize;
4883     i        = 1 - i;
4884   }
4885   *numCoveredPoints = joinSize;
4886   *coveredPoints    = join[i];
4887   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4888   PetscFunctionReturn(PETSC_SUCCESS);
4889 }
4890 
4891 /*@C
4892   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4893 
4894   Not Collective
4895 
4896   Input Parameters:
4897 + dm        - The `DMPLEX` object
4898 . numPoints - The number of input points for the join
4899 - points    - The input points
4900 
4901   Output Parameters:
4902 + numCoveredPoints - The number of points in the join
4903 - coveredPoints    - The points in the join
4904 
4905   Level: intermediate
4906 
4907   Fortran Notes:
4908   `converedPoints` must be declared with
4909 .vb
4910   PetscInt, pointer :: coveredPoints(:)
4911 .ve
4912 
4913   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4914 
4915 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4916 @*/
4917 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4918 {
4919   PetscFunctionBegin;
4920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4921   if (points) PetscAssertPointer(points, 3);
4922   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4923   PetscAssertPointer(coveredPoints, 5);
4924   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4925   if (numCoveredPoints) *numCoveredPoints = 0;
4926   PetscFunctionReturn(PETSC_SUCCESS);
4927 }
4928 
4929 /*@C
4930   DMPlexGetFullJoin - Get an array for the join of the set of points
4931 
4932   Not Collective
4933 
4934   Input Parameters:
4935 + dm        - The `DMPLEX` object
4936 . numPoints - The number of input points for the join
4937 - points    - The input points, its length is `numPoints`
4938 
4939   Output Parameters:
4940 + numCoveredPoints - The number of points in the join
4941 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4942 
4943   Level: intermediate
4944 
4945   Fortran Notes:
4946 .vb
4947   PetscInt, pointer :: coveredPints(:)
4948 .ve
4949 
4950 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4951 @*/
4952 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4953 {
4954   PetscInt *offsets, **closures;
4955   PetscInt *join[2];
4956   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4957   PetscInt  p, d, c, m, ms;
4958 
4959   PetscFunctionBegin;
4960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4961   PetscAssertPointer(points, 3);
4962   PetscAssertPointer(numCoveredPoints, 4);
4963   PetscAssertPointer(coveredPoints, 5);
4964 
4965   PetscCall(DMPlexGetDepth(dm, &depth));
4966   PetscCall(PetscCalloc1(numPoints, &closures));
4967   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4968   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4969   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4970   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4971   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4972 
4973   for (p = 0; p < numPoints; ++p) {
4974     PetscInt closureSize;
4975 
4976     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4977 
4978     offsets[p * (depth + 2) + 0] = 0;
4979     for (d = 0; d < depth + 1; ++d) {
4980       PetscInt pStart, pEnd, i;
4981 
4982       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4983       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4984         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4985           offsets[p * (depth + 2) + d + 1] = i;
4986           break;
4987         }
4988       }
4989       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4990     }
4991     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);
4992   }
4993   for (d = 0; d < depth + 1; ++d) {
4994     PetscInt dof;
4995 
4996     /* Copy in support of first point */
4997     dof = offsets[d + 1] - offsets[d];
4998     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4999     /* Check each successive cone */
5000     for (p = 1; p < numPoints && joinSize; ++p) {
5001       PetscInt newJoinSize = 0;
5002 
5003       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
5004       for (c = 0; c < dof; ++c) {
5005         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
5006 
5007         for (m = 0; m < joinSize; ++m) {
5008           if (point == join[i][m]) {
5009             join[1 - i][newJoinSize++] = point;
5010             break;
5011           }
5012         }
5013       }
5014       joinSize = newJoinSize;
5015       i        = 1 - i;
5016     }
5017     if (joinSize) break;
5018   }
5019   *numCoveredPoints = joinSize;
5020   *coveredPoints    = join[i];
5021   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5022   PetscCall(PetscFree(closures));
5023   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5024   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5025   PetscFunctionReturn(PETSC_SUCCESS);
5026 }
5027 
5028 /*@C
5029   DMPlexGetMeet - Get an array for the meet of the set of points
5030 
5031   Not Collective
5032 
5033   Input Parameters:
5034 + dm        - The `DMPLEX` object
5035 . numPoints - The number of input points for the meet
5036 - points    - The input points, of length `numPoints`
5037 
5038   Output Parameters:
5039 + numCoveringPoints - The number of points in the meet
5040 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5041 
5042   Level: intermediate
5043 
5044   Note:
5045   Currently, this is restricted to a single level meet
5046 
5047   Fortran Note:
5048   `coveringPoints` must be declared with
5049 .vb
5050   PetscInt, pointer :: coveringPoints(:)
5051 .ve
5052 
5053 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5054 @*/
5055 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5056 {
5057   DM_Plex  *mesh = (DM_Plex *)dm->data;
5058   PetscInt *meet[2];
5059   PetscInt  meetSize, i = 0;
5060   PetscInt  dof, off, p, c, m;
5061   PetscInt  maxConeSize;
5062 
5063   PetscFunctionBegin;
5064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5065   PetscAssertPointer(points, 3);
5066   PetscAssertPointer(numCoveringPoints, 4);
5067   PetscAssertPointer(coveringPoints, 5);
5068   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5069   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5070   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5071   /* Copy in cone of first point */
5072   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5073   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5074   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5075   /* Check each successive cone */
5076   for (p = 1; p < numPoints; ++p) {
5077     PetscInt newMeetSize = 0;
5078 
5079     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5080     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5081     for (c = 0; c < dof; ++c) {
5082       const PetscInt point = mesh->cones[off + c];
5083 
5084       for (m = 0; m < meetSize; ++m) {
5085         if (point == meet[i][m]) {
5086           meet[1 - i][newMeetSize++] = point;
5087           break;
5088         }
5089       }
5090     }
5091     meetSize = newMeetSize;
5092     i        = 1 - i;
5093   }
5094   *numCoveringPoints = meetSize;
5095   *coveringPoints    = meet[i];
5096   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5097   PetscFunctionReturn(PETSC_SUCCESS);
5098 }
5099 
5100 /*@C
5101   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5102 
5103   Not Collective
5104 
5105   Input Parameters:
5106 + dm        - The `DMPLEX` object
5107 . numPoints - The number of input points for the meet
5108 - points    - The input points
5109 
5110   Output Parameters:
5111 + numCoveredPoints - The number of points in the meet
5112 - coveredPoints    - The points in the meet
5113 
5114   Level: intermediate
5115 
5116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5117 @*/
5118 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5119 {
5120   PetscFunctionBegin;
5121   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5122   if (points) PetscAssertPointer(points, 3);
5123   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5124   PetscAssertPointer(coveredPoints, 5);
5125   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5126   if (numCoveredPoints) *numCoveredPoints = 0;
5127   PetscFunctionReturn(PETSC_SUCCESS);
5128 }
5129 
5130 /*@C
5131   DMPlexGetFullMeet - Get an array for the meet of the set of points
5132 
5133   Not Collective
5134 
5135   Input Parameters:
5136 + dm        - The `DMPLEX` object
5137 . numPoints - The number of input points for the meet
5138 - points    - The input points, of length  `numPoints`
5139 
5140   Output Parameters:
5141 + numCoveredPoints - The number of points in the meet
5142 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5143 
5144   Level: intermediate
5145 
5146   Fortran Notes:
5147   `coveredPoints` must be declared with
5148 .vb
5149   PetscInt, pointer :: coveredPoints(:)
5150 .ve
5151 
5152 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5153 @*/
5154 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5155 {
5156   PetscInt *offsets, **closures;
5157   PetscInt *meet[2];
5158   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5159   PetscInt  p, h, c, m, mc;
5160 
5161   PetscFunctionBegin;
5162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5163   PetscAssertPointer(points, 3);
5164   PetscAssertPointer(numCoveredPoints, 4);
5165   PetscAssertPointer(coveredPoints, 5);
5166 
5167   PetscCall(DMPlexGetDepth(dm, &height));
5168   PetscCall(PetscMalloc1(numPoints, &closures));
5169   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5170   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5171   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5172   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5173   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5174 
5175   for (p = 0; p < numPoints; ++p) {
5176     PetscInt closureSize;
5177 
5178     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5179 
5180     offsets[p * (height + 2) + 0] = 0;
5181     for (h = 0; h < height + 1; ++h) {
5182       PetscInt pStart, pEnd, i;
5183 
5184       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5185       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5186         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5187           offsets[p * (height + 2) + h + 1] = i;
5188           break;
5189         }
5190       }
5191       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5192     }
5193     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);
5194   }
5195   for (h = 0; h < height + 1; ++h) {
5196     PetscInt dof;
5197 
5198     /* Copy in cone of first point */
5199     dof = offsets[h + 1] - offsets[h];
5200     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5201     /* Check each successive cone */
5202     for (p = 1; p < numPoints && meetSize; ++p) {
5203       PetscInt newMeetSize = 0;
5204 
5205       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5206       for (c = 0; c < dof; ++c) {
5207         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5208 
5209         for (m = 0; m < meetSize; ++m) {
5210           if (point == meet[i][m]) {
5211             meet[1 - i][newMeetSize++] = point;
5212             break;
5213           }
5214         }
5215       }
5216       meetSize = newMeetSize;
5217       i        = 1 - i;
5218     }
5219     if (meetSize) break;
5220   }
5221   *numCoveredPoints = meetSize;
5222   *coveredPoints    = meet[i];
5223   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5224   PetscCall(PetscFree(closures));
5225   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5226   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5227   PetscFunctionReturn(PETSC_SUCCESS);
5228 }
5229 
5230 /*@
5231   DMPlexEqual - Determine if two `DM` have the same topology
5232 
5233   Not Collective
5234 
5235   Input Parameters:
5236 + dmA - A `DMPLEX` object
5237 - dmB - A `DMPLEX` object
5238 
5239   Output Parameter:
5240 . equal - `PETSC_TRUE` if the topologies are identical
5241 
5242   Level: intermediate
5243 
5244   Note:
5245   We are not solving graph isomorphism, so we do not permute.
5246 
5247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5248 @*/
5249 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5250 {
5251   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5252 
5253   PetscFunctionBegin;
5254   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5255   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5256   PetscAssertPointer(equal, 3);
5257 
5258   *equal = PETSC_FALSE;
5259   PetscCall(DMPlexGetDepth(dmA, &depth));
5260   PetscCall(DMPlexGetDepth(dmB, &depthB));
5261   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5262   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5263   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5264   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5265   for (p = pStart; p < pEnd; ++p) {
5266     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5267     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5268 
5269     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5270     PetscCall(DMPlexGetCone(dmA, p, &cone));
5271     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5272     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5273     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5274     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5275     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5276     for (c = 0; c < coneSize; ++c) {
5277       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5278       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5279     }
5280     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5281     PetscCall(DMPlexGetSupport(dmA, p, &support));
5282     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5283     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5284     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5285     for (s = 0; s < supportSize; ++s) {
5286       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5287     }
5288   }
5289   *equal = PETSC_TRUE;
5290   PetscFunctionReturn(PETSC_SUCCESS);
5291 }
5292 
5293 /*@
5294   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5295 
5296   Not Collective
5297 
5298   Input Parameters:
5299 + dm         - The `DMPLEX`
5300 . cellDim    - The cell dimension
5301 - numCorners - The number of vertices on a cell
5302 
5303   Output Parameter:
5304 . numFaceVertices - The number of vertices on a face
5305 
5306   Level: developer
5307 
5308   Note:
5309   Of course this can only work for a restricted set of symmetric shapes
5310 
5311 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5312 @*/
5313 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5314 {
5315   MPI_Comm comm;
5316 
5317   PetscFunctionBegin;
5318   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5319   PetscAssertPointer(numFaceVertices, 4);
5320   switch (cellDim) {
5321   case 0:
5322     *numFaceVertices = 0;
5323     break;
5324   case 1:
5325     *numFaceVertices = 1;
5326     break;
5327   case 2:
5328     switch (numCorners) {
5329     case 3:                 /* triangle */
5330       *numFaceVertices = 2; /* Edge has 2 vertices */
5331       break;
5332     case 4:                 /* quadrilateral */
5333       *numFaceVertices = 2; /* Edge has 2 vertices */
5334       break;
5335     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5336       *numFaceVertices = 3; /* Edge has 3 vertices */
5337       break;
5338     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5339       *numFaceVertices = 3; /* Edge has 3 vertices */
5340       break;
5341     default:
5342       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5343     }
5344     break;
5345   case 3:
5346     switch (numCorners) {
5347     case 4:                 /* tetradehdron */
5348       *numFaceVertices = 3; /* Face has 3 vertices */
5349       break;
5350     case 6:                 /* tet cohesive cells */
5351       *numFaceVertices = 4; /* Face has 4 vertices */
5352       break;
5353     case 8:                 /* hexahedron */
5354       *numFaceVertices = 4; /* Face has 4 vertices */
5355       break;
5356     case 9:                 /* tet cohesive Lagrange cells */
5357       *numFaceVertices = 6; /* Face has 6 vertices */
5358       break;
5359     case 10:                /* quadratic tetrahedron */
5360       *numFaceVertices = 6; /* Face has 6 vertices */
5361       break;
5362     case 12:                /* hex cohesive Lagrange cells */
5363       *numFaceVertices = 6; /* Face has 6 vertices */
5364       break;
5365     case 18:                /* quadratic tet cohesive Lagrange cells */
5366       *numFaceVertices = 6; /* Face has 6 vertices */
5367       break;
5368     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5369       *numFaceVertices = 9; /* Face has 9 vertices */
5370       break;
5371     default:
5372       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5373     }
5374     break;
5375   default:
5376     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5377   }
5378   PetscFunctionReturn(PETSC_SUCCESS);
5379 }
5380 
5381 /*@
5382   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5383 
5384   Not Collective
5385 
5386   Input Parameter:
5387 . dm - The `DMPLEX` object
5388 
5389   Output Parameter:
5390 . depthLabel - The `DMLabel` recording point depth
5391 
5392   Level: developer
5393 
5394 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5395 @*/
5396 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5397 {
5398   PetscFunctionBegin;
5399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5400   PetscAssertPointer(depthLabel, 2);
5401   *depthLabel = dm->depthLabel;
5402   PetscFunctionReturn(PETSC_SUCCESS);
5403 }
5404 
5405 /*@
5406   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5407 
5408   Not Collective
5409 
5410   Input Parameter:
5411 . dm - The `DMPLEX` object
5412 
5413   Output Parameter:
5414 . depth - The number of strata (breadth first levels) in the DAG
5415 
5416   Level: developer
5417 
5418   Notes:
5419   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5420 
5421   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5422 
5423   An empty mesh gives -1.
5424 
5425 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5426 @*/
5427 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5428 {
5429   DM_Plex *mesh = (DM_Plex *)dm->data;
5430   DMLabel  label;
5431   PetscInt d = -1;
5432 
5433   PetscFunctionBegin;
5434   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5435   PetscAssertPointer(depth, 2);
5436   if (mesh->tr) {
5437     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5438   } else {
5439     PetscCall(DMPlexGetDepthLabel(dm, &label));
5440     // Allow missing depths
5441     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5442     *depth = d;
5443   }
5444   PetscFunctionReturn(PETSC_SUCCESS);
5445 }
5446 
5447 /*@
5448   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5449 
5450   Not Collective
5451 
5452   Input Parameters:
5453 + dm    - The `DMPLEX` object
5454 - depth - The requested depth
5455 
5456   Output Parameters:
5457 + start - The first point at this `depth`
5458 - end   - One beyond the last point at this `depth`
5459 
5460   Level: developer
5461 
5462   Notes:
5463   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5464   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5465   higher dimension, e.g., "edges".
5466 
5467 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5468 @*/
5469 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5470 {
5471   DM_Plex *mesh = (DM_Plex *)dm->data;
5472   DMLabel  label;
5473   PetscInt pStart, pEnd;
5474 
5475   PetscFunctionBegin;
5476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5477   if (start) {
5478     PetscAssertPointer(start, 3);
5479     *start = 0;
5480   }
5481   if (end) {
5482     PetscAssertPointer(end, 4);
5483     *end = 0;
5484   }
5485   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5486   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5487   if (depth < 0) {
5488     if (start) *start = pStart;
5489     if (end) *end = pEnd;
5490     PetscFunctionReturn(PETSC_SUCCESS);
5491   }
5492   if (mesh->tr) {
5493     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5494   } else {
5495     PetscCall(DMPlexGetDepthLabel(dm, &label));
5496     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5497     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5498   }
5499   PetscFunctionReturn(PETSC_SUCCESS);
5500 }
5501 
5502 /*@
5503   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5504 
5505   Not Collective
5506 
5507   Input Parameters:
5508 + dm     - The `DMPLEX` object
5509 - height - The requested height
5510 
5511   Output Parameters:
5512 + start - The first point at this `height`
5513 - end   - One beyond the last point at this `height`
5514 
5515   Level: developer
5516 
5517   Notes:
5518   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5519   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5520   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5521 
5522 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5523 @*/
5524 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5525 {
5526   DMLabel  label;
5527   PetscInt depth, pStart, pEnd;
5528 
5529   PetscFunctionBegin;
5530   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5531   if (start) {
5532     PetscAssertPointer(start, 3);
5533     *start = 0;
5534   }
5535   if (end) {
5536     PetscAssertPointer(end, 4);
5537     *end = 0;
5538   }
5539   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5540   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5541   if (height < 0) {
5542     if (start) *start = pStart;
5543     if (end) *end = pEnd;
5544     PetscFunctionReturn(PETSC_SUCCESS);
5545   }
5546   PetscCall(DMPlexGetDepthLabel(dm, &label));
5547   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5548   else PetscCall(DMGetDimension(dm, &depth));
5549   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5550   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5551   PetscFunctionReturn(PETSC_SUCCESS);
5552 }
5553 
5554 /*@
5555   DMPlexGetPointDepth - Get the `depth` of a given point
5556 
5557   Not Collective
5558 
5559   Input Parameters:
5560 + dm    - The `DMPLEX` object
5561 - point - The point
5562 
5563   Output Parameter:
5564 . depth - The depth of the `point`
5565 
5566   Level: intermediate
5567 
5568 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5569 @*/
5570 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5571 {
5572   PetscFunctionBegin;
5573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5574   PetscAssertPointer(depth, 3);
5575   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5576   PetscFunctionReturn(PETSC_SUCCESS);
5577 }
5578 
5579 /*@
5580   DMPlexGetPointHeight - Get the `height` of a given point
5581 
5582   Not Collective
5583 
5584   Input Parameters:
5585 + dm    - The `DMPLEX` object
5586 - point - The point
5587 
5588   Output Parameter:
5589 . height - The height of the `point`
5590 
5591   Level: intermediate
5592 
5593 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5594 @*/
5595 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5596 {
5597   PetscInt n, pDepth;
5598 
5599   PetscFunctionBegin;
5600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5601   PetscAssertPointer(height, 3);
5602   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5603   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5604   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5605   PetscFunctionReturn(PETSC_SUCCESS);
5606 }
5607 
5608 /*@
5609   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5610 
5611   Not Collective
5612 
5613   Input Parameter:
5614 . dm - The `DMPLEX` object
5615 
5616   Output Parameter:
5617 . celltypeLabel - The `DMLabel` recording cell polytope type
5618 
5619   Level: developer
5620 
5621   Note:
5622   This function will trigger automatica computation of cell types. This can be disabled by calling
5623   `DMCreateLabel`(dm, "celltype") beforehand.
5624 
5625 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5626 @*/
5627 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5628 {
5629   PetscFunctionBegin;
5630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5631   PetscAssertPointer(celltypeLabel, 2);
5632   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5633   *celltypeLabel = dm->celltypeLabel;
5634   PetscFunctionReturn(PETSC_SUCCESS);
5635 }
5636 
5637 /*@
5638   DMPlexGetCellType - Get the polytope type of a given cell
5639 
5640   Not Collective
5641 
5642   Input Parameters:
5643 + dm   - The `DMPLEX` object
5644 - cell - The cell
5645 
5646   Output Parameter:
5647 . celltype - The polytope type of the cell
5648 
5649   Level: intermediate
5650 
5651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5652 @*/
5653 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5654 {
5655   DM_Plex *mesh = (DM_Plex *)dm->data;
5656   DMLabel  label;
5657   PetscInt ct;
5658 
5659   PetscFunctionBegin;
5660   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5661   PetscAssertPointer(celltype, 3);
5662   if (mesh->tr) {
5663     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5664   } else {
5665     PetscInt pStart, pEnd;
5666 
5667     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5668     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5669       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5670       if (pEnd <= pStart) {
5671         *celltype = DM_POLYTOPE_UNKNOWN;
5672         PetscFunctionReturn(PETSC_SUCCESS);
5673       }
5674       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5675       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5676       for (PetscInt p = pStart; p < pEnd; p++) {
5677         PetscCall(DMLabelGetValue(label, p, &ct));
5678         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5679       }
5680     }
5681     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5682     if (PetscDefined(USE_DEBUG)) {
5683       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5684       PetscCall(DMLabelGetValue(label, cell, &ct));
5685       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5686       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5687     }
5688   }
5689   PetscFunctionReturn(PETSC_SUCCESS);
5690 }
5691 
5692 /*@
5693   DMPlexSetCellType - Set the polytope type of a given cell
5694 
5695   Not Collective
5696 
5697   Input Parameters:
5698 + dm       - The `DMPLEX` object
5699 . cell     - The cell
5700 - celltype - The polytope type of the cell
5701 
5702   Level: advanced
5703 
5704   Note:
5705   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5706   is executed. This function will override the computed type. However, if automatic classification will not succeed
5707   and a user wants to manually specify all types, the classification must be disabled by calling
5708   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5709 
5710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5711 @*/
5712 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5713 {
5714   DM_Plex *mesh = (DM_Plex *)dm->data;
5715   DMLabel  label;
5716   PetscInt pStart, pEnd;
5717 
5718   PetscFunctionBegin;
5719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5720   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5721   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5722   PetscCall(DMLabelSetValue(label, cell, celltype));
5723   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5724   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5725   PetscFunctionReturn(PETSC_SUCCESS);
5726 }
5727 
5728 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5729 {
5730   PetscSection section;
5731   PetscInt     maxHeight;
5732   const char  *prefix;
5733 
5734   PetscFunctionBegin;
5735   PetscCall(DMClone(dm, cdm));
5736   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5737   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5738   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5739   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5740   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5741   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5742   PetscCall(DMSetLocalSection(*cdm, section));
5743   PetscCall(PetscSectionDestroy(&section));
5744 
5745   PetscCall(DMSetNumFields(*cdm, 1));
5746   PetscCall(DMCreateDS(*cdm));
5747   (*cdm)->cloneOpts = PETSC_TRUE;
5748   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5749   PetscFunctionReturn(PETSC_SUCCESS);
5750 }
5751 
5752 PetscErrorCode DMCreateCellCoordinateDM_Plex(DM dm, DM *cdm)
5753 {
5754   DM           cgcdm;
5755   PetscSection section;
5756   const char  *prefix;
5757 
5758   PetscFunctionBegin;
5759   PetscCall(DMGetCoordinateDM(dm, &cgcdm));
5760   PetscCall(DMClone(cgcdm, cdm));
5761   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5762   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5763   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cellcdm_"));
5764   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5765   PetscCall(DMSetLocalSection(*cdm, section));
5766   PetscCall(PetscSectionDestroy(&section));
5767   PetscCall(DMSetNumFields(*cdm, 1));
5768   PetscCall(DMCreateDS(*cdm));
5769   (*cdm)->cloneOpts = PETSC_TRUE;
5770   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5771   PetscFunctionReturn(PETSC_SUCCESS);
5772 }
5773 
5774 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5775 {
5776   Vec coordsLocal, cellCoordsLocal;
5777   DM  coordsDM, cellCoordsDM;
5778 
5779   PetscFunctionBegin;
5780   *field = NULL;
5781   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5782   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5783   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5784   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5785   if (coordsLocal && coordsDM) {
5786     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5787     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5788   }
5789   PetscFunctionReturn(PETSC_SUCCESS);
5790 }
5791 
5792 /*@
5793   DMPlexGetConeSection - Return a section which describes the layout of cone data
5794 
5795   Not Collective
5796 
5797   Input Parameter:
5798 . dm - The `DMPLEX` object
5799 
5800   Output Parameter:
5801 . section - The `PetscSection` object
5802 
5803   Level: developer
5804 
5805 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5806 @*/
5807 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5808 {
5809   DM_Plex *mesh = (DM_Plex *)dm->data;
5810 
5811   PetscFunctionBegin;
5812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5813   if (section) *section = mesh->coneSection;
5814   PetscFunctionReturn(PETSC_SUCCESS);
5815 }
5816 
5817 /*@
5818   DMPlexGetSupportSection - Return a section which describes the layout of support data
5819 
5820   Not Collective
5821 
5822   Input Parameter:
5823 . dm - The `DMPLEX` object
5824 
5825   Output Parameter:
5826 . section - The `PetscSection` object
5827 
5828   Level: developer
5829 
5830 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5831 @*/
5832 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5833 {
5834   DM_Plex *mesh = (DM_Plex *)dm->data;
5835 
5836   PetscFunctionBegin;
5837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5838   if (section) *section = mesh->supportSection;
5839   PetscFunctionReturn(PETSC_SUCCESS);
5840 }
5841 
5842 /*@C
5843   DMPlexGetCones - Return cone data
5844 
5845   Not Collective
5846 
5847   Input Parameter:
5848 . dm - The `DMPLEX` object
5849 
5850   Output Parameter:
5851 . cones - The cone for each point
5852 
5853   Level: developer
5854 
5855 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5856 @*/
5857 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5858 {
5859   DM_Plex *mesh = (DM_Plex *)dm->data;
5860 
5861   PetscFunctionBegin;
5862   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5863   if (cones) *cones = mesh->cones;
5864   PetscFunctionReturn(PETSC_SUCCESS);
5865 }
5866 
5867 /*@C
5868   DMPlexGetConeOrientations - Return cone orientation data
5869 
5870   Not Collective
5871 
5872   Input Parameter:
5873 . dm - The `DMPLEX` object
5874 
5875   Output Parameter:
5876 . coneOrientations - The array of cone orientations for all points
5877 
5878   Level: developer
5879 
5880   Notes:
5881   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5882   as returned by `DMPlexGetConeOrientation()`.
5883 
5884   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5885 
5886 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5887 @*/
5888 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5889 {
5890   DM_Plex *mesh = (DM_Plex *)dm->data;
5891 
5892   PetscFunctionBegin;
5893   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5894   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5895   PetscFunctionReturn(PETSC_SUCCESS);
5896 }
5897 
5898 /* FEM Support */
5899 
5900 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5901 {
5902   PetscInt depth;
5903 
5904   PetscFunctionBegin;
5905   PetscCall(DMPlexGetDepth(plex, &depth));
5906   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5907   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5908   PetscFunctionReturn(PETSC_SUCCESS);
5909 }
5910 
5911 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5912 {
5913   PetscInt depth;
5914 
5915   PetscFunctionBegin;
5916   PetscCall(DMPlexGetDepth(plex, &depth));
5917   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5918   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5919   PetscFunctionReturn(PETSC_SUCCESS);
5920 }
5921 
5922 /*
5923  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5924  representing a line in the section.
5925 */
5926 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5927 {
5928   PetscObject  obj;
5929   PetscClassId id;
5930   PetscFE      fe = NULL;
5931 
5932   PetscFunctionBeginHot;
5933   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5934   PetscCall(DMGetField(dm, field, NULL, &obj));
5935   PetscCall(PetscObjectGetClassId(obj, &id));
5936   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5937 
5938   if (!fe) {
5939     /* Assume the full interpolated mesh is in the chart; lines in particular */
5940     /* An order k SEM disc has k-1 dofs on an edge */
5941     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5942     *k = *k / *Nc + 1;
5943   } else {
5944     PetscInt       dual_space_size, dim;
5945     PetscDualSpace dsp;
5946 
5947     PetscCall(DMGetDimension(dm, &dim));
5948     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5949     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5950     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5951     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5952     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5953   }
5954   PetscFunctionReturn(PETSC_SUCCESS);
5955 }
5956 
5957 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5958 {
5959   PetscFunctionBeginHot;
5960   if (tensor) {
5961     *dof = PetscPowInt(k + 1, dim);
5962   } else {
5963     switch (dim) {
5964     case 1:
5965       *dof = k + 1;
5966       break;
5967     case 2:
5968       *dof = ((k + 1) * (k + 2)) / 2;
5969       break;
5970     case 3:
5971       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5972       break;
5973     default:
5974       *dof = 0;
5975     }
5976   }
5977   PetscFunctionReturn(PETSC_SUCCESS);
5978 }
5979 
5980 /*@
5981   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5982   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5983   section provided (or the section of the `DM`).
5984 
5985   Input Parameters:
5986 + dm      - The `DM`
5987 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5988 - section - The `PetscSection` to reorder, or `NULL` for the default section
5989 
5990   Example:
5991   A typical interpolated single-quad mesh might order points as
5992 .vb
5993   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5994 
5995   v4 -- e6 -- v3
5996   |           |
5997   e7    c0    e8
5998   |           |
5999   v1 -- e5 -- v2
6000 .ve
6001 
6002   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
6003   dofs in the order of points, e.g.,
6004 .vb
6005     c0 -> [0,1,2,3]
6006     v1 -> [4]
6007     ...
6008     e5 -> [8, 9]
6009 .ve
6010 
6011   which corresponds to the dofs
6012 .vb
6013     6   10  11  7
6014     13  2   3   15
6015     12  0   1   14
6016     4   8   9   5
6017 .ve
6018 
6019   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
6020 .vb
6021   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
6022 .ve
6023 
6024   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
6025 .vb
6026    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6027 .ve
6028 
6029   Level: developer
6030 
6031   Notes:
6032   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6033   degree of the basis.
6034 
6035   This is required to run with libCEED.
6036 
6037 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6038 @*/
6039 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6040 {
6041   DMLabel   label;
6042   PetscInt  dim, depth = -1, eStart = -1, Nf;
6043   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6044 
6045   PetscFunctionBegin;
6046   PetscCall(DMGetDimension(dm, &dim));
6047   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6048   if (point < 0) {
6049     PetscInt sStart, sEnd;
6050 
6051     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6052     point = sEnd - sStart ? sStart : point;
6053   }
6054   PetscCall(DMPlexGetDepthLabel(dm, &label));
6055   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6056   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6057   if (depth == 1) {
6058     eStart = point;
6059   } else if (depth == dim) {
6060     const PetscInt *cone;
6061 
6062     PetscCall(DMPlexGetCone(dm, point, &cone));
6063     if (dim == 2) eStart = cone[0];
6064     else if (dim == 3) {
6065       const PetscInt *cone2;
6066       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6067       eStart = cone2[0];
6068     } 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);
6069   } 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);
6070 
6071   PetscCall(PetscSectionGetNumFields(section, &Nf));
6072   for (PetscInt d = 1; d <= dim; d++) {
6073     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6074     PetscInt *perm;
6075 
6076     for (f = 0; f < Nf; ++f) {
6077       PetscInt dof;
6078 
6079       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6080       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6081       if (!continuous && d < dim) continue;
6082       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6083       size += dof * Nc;
6084     }
6085     PetscCall(PetscMalloc1(size, &perm));
6086     for (f = 0; f < Nf; ++f) {
6087       switch (d) {
6088       case 1:
6089         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6090         if (!continuous && d < dim) continue;
6091         /*
6092          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6093          We want              [ vtx0; edge of length k-1; vtx1 ]
6094          */
6095         if (continuous) {
6096           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6097           for (i = 0; i < k - 1; i++)
6098             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6099           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6100           foffset = offset;
6101         } else {
6102           PetscInt dof;
6103 
6104           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6105           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6106           foffset = offset;
6107         }
6108         break;
6109       case 2:
6110         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6111         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6112         if (!continuous && d < dim) continue;
6113         /* The SEM order is
6114 
6115          v_lb, {e_b}, v_rb,
6116          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6117          v_lt, reverse {e_t}, v_rt
6118          */
6119         if (continuous) {
6120           const PetscInt of   = 0;
6121           const PetscInt oeb  = of + PetscSqr(k - 1);
6122           const PetscInt oer  = oeb + (k - 1);
6123           const PetscInt oet  = oer + (k - 1);
6124           const PetscInt oel  = oet + (k - 1);
6125           const PetscInt ovlb = oel + (k - 1);
6126           const PetscInt ovrb = ovlb + 1;
6127           const PetscInt ovrt = ovrb + 1;
6128           const PetscInt ovlt = ovrt + 1;
6129           PetscInt       o;
6130 
6131           /* bottom */
6132           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6133           for (o = oeb; o < oer; ++o)
6134             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6135           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6136           /* middle */
6137           for (i = 0; i < k - 1; ++i) {
6138             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6139             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6140               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6141             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6142           }
6143           /* top */
6144           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6145           for (o = oel - 1; o >= oet; --o)
6146             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6147           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6148           foffset = offset;
6149         } else {
6150           PetscInt dof;
6151 
6152           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6153           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6154           foffset = offset;
6155         }
6156         break;
6157       case 3:
6158         /* The original hex closure is
6159 
6160          {c,
6161          f_b, f_t, f_f, f_b, f_r, f_l,
6162          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6163          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6164          */
6165         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6166         if (!continuous && d < dim) continue;
6167         /* The SEM order is
6168          Bottom Slice
6169          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6170          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6171          v_blb, {e_bb}, v_brb,
6172 
6173          Middle Slice (j)
6174          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6175          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6176          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6177 
6178          Top Slice
6179          v_tlf, {e_tf}, v_trf,
6180          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6181          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6182          */
6183         if (continuous) {
6184           const PetscInt oc    = 0;
6185           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6186           const PetscInt oft   = ofb + PetscSqr(k - 1);
6187           const PetscInt off   = oft + PetscSqr(k - 1);
6188           const PetscInt ofk   = off + PetscSqr(k - 1);
6189           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6190           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6191           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6192           const PetscInt oebb  = oebl + (k - 1);
6193           const PetscInt oebr  = oebb + (k - 1);
6194           const PetscInt oebf  = oebr + (k - 1);
6195           const PetscInt oetf  = oebf + (k - 1);
6196           const PetscInt oetr  = oetf + (k - 1);
6197           const PetscInt oetb  = oetr + (k - 1);
6198           const PetscInt oetl  = oetb + (k - 1);
6199           const PetscInt oerf  = oetl + (k - 1);
6200           const PetscInt oelf  = oerf + (k - 1);
6201           const PetscInt oelb  = oelf + (k - 1);
6202           const PetscInt oerb  = oelb + (k - 1);
6203           const PetscInt ovblf = oerb + (k - 1);
6204           const PetscInt ovblb = ovblf + 1;
6205           const PetscInt ovbrb = ovblb + 1;
6206           const PetscInt ovbrf = ovbrb + 1;
6207           const PetscInt ovtlf = ovbrf + 1;
6208           const PetscInt ovtrf = ovtlf + 1;
6209           const PetscInt ovtrb = ovtrf + 1;
6210           const PetscInt ovtlb = ovtrb + 1;
6211           PetscInt       o, n;
6212 
6213           /* Bottom Slice */
6214           /*   bottom */
6215           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6216           for (o = oetf - 1; o >= oebf; --o)
6217             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6218           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6219           /*   middle */
6220           for (i = 0; i < k - 1; ++i) {
6221             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6222             for (n = 0; n < k - 1; ++n) {
6223               o = ofb + n * (k - 1) + i;
6224               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6225             }
6226             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6227           }
6228           /*   top */
6229           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6230           for (o = oebb; o < oebr; ++o)
6231             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6232           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6233 
6234           /* Middle Slice */
6235           for (j = 0; j < k - 1; ++j) {
6236             /*   bottom */
6237             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6238             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6239               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6240             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6241             /*   middle */
6242             for (i = 0; i < k - 1; ++i) {
6243               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6244               for (n = 0; n < k - 1; ++n)
6245                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6246               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6247             }
6248             /*   top */
6249             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6250             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6251               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6252             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6253           }
6254 
6255           /* Top Slice */
6256           /*   bottom */
6257           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6258           for (o = oetf; o < oetr; ++o)
6259             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6260           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6261           /*   middle */
6262           for (i = 0; i < k - 1; ++i) {
6263             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6264             for (n = 0; n < k - 1; ++n)
6265               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6266             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6267           }
6268           /*   top */
6269           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6270           for (o = oetl - 1; o >= oetb; --o)
6271             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6272           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6273 
6274           foffset = offset;
6275         } else {
6276           PetscInt dof;
6277 
6278           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6279           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6280           foffset = offset;
6281         }
6282         break;
6283       default:
6284         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6285       }
6286     }
6287     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6288     /* Check permutation */
6289     {
6290       PetscInt *check;
6291 
6292       PetscCall(PetscMalloc1(size, &check));
6293       for (i = 0; i < size; ++i) {
6294         check[i] = -1;
6295         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6296       }
6297       for (i = 0; i < size; ++i) check[perm[i]] = i;
6298       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6299       PetscCall(PetscFree(check));
6300     }
6301     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6302     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6303       PetscInt *loc_perm;
6304       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6305       for (PetscInt i = 0; i < size; i++) {
6306         loc_perm[i]        = perm[i];
6307         loc_perm[size + i] = size + perm[i];
6308       }
6309       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6310     }
6311   }
6312   PetscFunctionReturn(PETSC_SUCCESS);
6313 }
6314 
6315 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6316 {
6317   PetscDS  prob;
6318   PetscInt depth, Nf, h;
6319   DMLabel  label;
6320 
6321   PetscFunctionBeginHot;
6322   PetscCall(DMGetDS(dm, &prob));
6323   Nf      = prob->Nf;
6324   label   = dm->depthLabel;
6325   *dspace = NULL;
6326   if (field < Nf) {
6327     PetscObject disc = prob->disc[field];
6328 
6329     if (disc->classid == PETSCFE_CLASSID) {
6330       PetscDualSpace dsp;
6331 
6332       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6333       PetscCall(DMLabelGetNumValues(label, &depth));
6334       PetscCall(DMLabelGetValue(label, point, &h));
6335       h = depth - 1 - h;
6336       if (h) {
6337         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6338       } else {
6339         *dspace = dsp;
6340       }
6341     }
6342   }
6343   PetscFunctionReturn(PETSC_SUCCESS);
6344 }
6345 
6346 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6347 {
6348   PetscScalar       *array;
6349   const PetscScalar *vArray;
6350   const PetscInt    *cone, *coneO;
6351   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6352 
6353   PetscFunctionBeginHot;
6354   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6355   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6356   PetscCall(DMPlexGetCone(dm, point, &cone));
6357   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6358   if (!values || !*values) {
6359     if ((point >= pStart) && (point < pEnd)) {
6360       PetscInt dof;
6361 
6362       PetscCall(PetscSectionGetDof(section, point, &dof));
6363       size += dof;
6364     }
6365     for (p = 0; p < numPoints; ++p) {
6366       const PetscInt cp = cone[p];
6367       PetscInt       dof;
6368 
6369       if ((cp < pStart) || (cp >= pEnd)) continue;
6370       PetscCall(PetscSectionGetDof(section, cp, &dof));
6371       size += dof;
6372     }
6373     if (!values) {
6374       if (csize) *csize = size;
6375       PetscFunctionReturn(PETSC_SUCCESS);
6376     }
6377     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6378   } else {
6379     array = *values;
6380   }
6381   size = 0;
6382   PetscCall(VecGetArrayRead(v, &vArray));
6383   if ((point >= pStart) && (point < pEnd)) {
6384     PetscInt           dof, off, d;
6385     const PetscScalar *varr;
6386 
6387     PetscCall(PetscSectionGetDof(section, point, &dof));
6388     PetscCall(PetscSectionGetOffset(section, point, &off));
6389     varr = PetscSafePointerPlusOffset(vArray, off);
6390     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6391     size += dof;
6392   }
6393   for (p = 0; p < numPoints; ++p) {
6394     const PetscInt     cp = cone[p];
6395     PetscInt           o  = coneO[p];
6396     PetscInt           dof, off, d;
6397     const PetscScalar *varr;
6398 
6399     if ((cp < pStart) || (cp >= pEnd)) continue;
6400     PetscCall(PetscSectionGetDof(section, cp, &dof));
6401     PetscCall(PetscSectionGetOffset(section, cp, &off));
6402     varr = PetscSafePointerPlusOffset(vArray, off);
6403     if (o >= 0) {
6404       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6405     } else {
6406       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6407     }
6408     size += dof;
6409   }
6410   PetscCall(VecRestoreArrayRead(v, &vArray));
6411   if (!*values) {
6412     if (csize) *csize = size;
6413     *values = array;
6414   } else {
6415     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6416     *csize = size;
6417   }
6418   PetscFunctionReturn(PETSC_SUCCESS);
6419 }
6420 
6421 /* Compress out points not in the section */
6422 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6423 {
6424   const PetscInt np = *numPoints;
6425   PetscInt       pStart, pEnd, p, q;
6426 
6427   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6428   for (p = 0, q = 0; p < np; ++p) {
6429     const PetscInt r = points[p * 2];
6430     if ((r >= pStart) && (r < pEnd)) {
6431       points[q * 2]     = r;
6432       points[q * 2 + 1] = points[p * 2 + 1];
6433       ++q;
6434     }
6435   }
6436   *numPoints = q;
6437   return PETSC_SUCCESS;
6438 }
6439 
6440 /* Compressed closure does not apply closure permutation */
6441 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6442 {
6443   const PetscInt *cla = NULL;
6444   PetscInt        np, *pts = NULL;
6445 
6446   PetscFunctionBeginHot;
6447   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6448   if (!ornt && *clPoints) {
6449     PetscInt dof, off;
6450 
6451     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6452     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6453     PetscCall(ISGetIndices(*clPoints, &cla));
6454     np  = dof / 2;
6455     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6456   } else {
6457     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6458     PetscCall(CompressPoints_Private(section, &np, pts));
6459   }
6460   *numPoints = np;
6461   *points    = pts;
6462   *clp       = cla;
6463   PetscFunctionReturn(PETSC_SUCCESS);
6464 }
6465 
6466 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6467 {
6468   PetscFunctionBeginHot;
6469   if (!*clPoints) {
6470     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6471   } else {
6472     PetscCall(ISRestoreIndices(*clPoints, clp));
6473   }
6474   *numPoints = 0;
6475   *points    = NULL;
6476   *clSec     = NULL;
6477   *clPoints  = NULL;
6478   *clp       = NULL;
6479   PetscFunctionReturn(PETSC_SUCCESS);
6480 }
6481 
6482 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6483 {
6484   PetscInt            offset = 0, p;
6485   const PetscInt    **perms  = NULL;
6486   const PetscScalar **flips  = NULL;
6487 
6488   PetscFunctionBeginHot;
6489   *size = 0;
6490   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6491   for (p = 0; p < numPoints; p++) {
6492     const PetscInt     point = points[2 * p];
6493     const PetscInt    *perm  = perms ? perms[p] : NULL;
6494     const PetscScalar *flip  = flips ? flips[p] : NULL;
6495     PetscInt           dof, off, d;
6496     const PetscScalar *varr;
6497 
6498     PetscCall(PetscSectionGetDof(section, point, &dof));
6499     PetscCall(PetscSectionGetOffset(section, point, &off));
6500     varr = PetscSafePointerPlusOffset(vArray, off);
6501     if (clperm) {
6502       if (perm) {
6503         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6504       } else {
6505         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6506       }
6507       if (flip) {
6508         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6509       }
6510     } else {
6511       if (perm) {
6512         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6513       } else {
6514         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6515       }
6516       if (flip) {
6517         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6518       }
6519     }
6520     offset += dof;
6521   }
6522   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6523   *size = offset;
6524   PetscFunctionReturn(PETSC_SUCCESS);
6525 }
6526 
6527 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[])
6528 {
6529   PetscInt offset = 0, f;
6530 
6531   PetscFunctionBeginHot;
6532   *size = 0;
6533   for (f = 0; f < numFields; ++f) {
6534     PetscInt            p;
6535     const PetscInt    **perms = NULL;
6536     const PetscScalar **flips = NULL;
6537 
6538     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6539     for (p = 0; p < numPoints; p++) {
6540       const PetscInt     point = points[2 * p];
6541       PetscInt           fdof, foff, b;
6542       const PetscScalar *varr;
6543       const PetscInt    *perm = perms ? perms[p] : NULL;
6544       const PetscScalar *flip = flips ? flips[p] : NULL;
6545 
6546       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6547       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6548       varr = &vArray[foff];
6549       if (clperm) {
6550         if (perm) {
6551           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6552         } else {
6553           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6554         }
6555         if (flip) {
6556           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6557         }
6558       } else {
6559         if (perm) {
6560           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6561         } else {
6562           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6563         }
6564         if (flip) {
6565           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6566         }
6567       }
6568       offset += fdof;
6569     }
6570     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6571   }
6572   *size = offset;
6573   PetscFunctionReturn(PETSC_SUCCESS);
6574 }
6575 
6576 /*@C
6577   DMPlexVecGetOrientedClosure - Get an array of the values on the closure of 'point' with a given orientation, optionally applying the closure permutation.
6578 
6579   Not collective
6580 
6581   Input Parameters:
6582 + dm        - The `DM`
6583 . section   - The section describing the layout in `v`, or `NULL` to use the default section
6584 . useClPerm - Flag for whether the provided closure permutation should be applied to the values
6585 . v         - The local vector
6586 . point     - The point in the `DM`
6587 - ornt      - The orientation of the cell, an integer giving the prescription for cone traversal. Typically, this will be 0.
6588 
6589   Input/Output Parameters:
6590 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6591 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6592            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6593 
6594   Level: advanced
6595 
6596   Notes:
6597   `DMPlexVecGetOrientedClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6598   calling function. This is because `DMPlexVecGetOrientedClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6599   assembly function, and a user may already have allocated storage for this operation.
6600 
6601   Fortran Notes:
6602   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6603   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6604 
6605   `values` must be declared with
6606 .vb
6607   PetscScalar,dimension(:),pointer   :: values
6608 .ve
6609   and it will be allocated internally by PETSc to hold the values returned
6610 
6611 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexGetCellCoordinates()`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`
6612 @*/
6613 PetscErrorCode DMPlexVecGetOrientedClosure(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6614 {
6615   PetscSection    clSection;
6616   IS              clPoints;
6617   PetscInt       *points = NULL;
6618   const PetscInt *clp, *perm = NULL;
6619   PetscInt        depth, numFields, numPoints, asize;
6620 
6621   PetscFunctionBeginHot;
6622   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6623   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6624   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6625   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6626   PetscCall(DMPlexGetDepth(dm, &depth));
6627   PetscCall(PetscSectionGetNumFields(section, &numFields));
6628   if (depth == 1 && numFields < 2) {
6629     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6630     PetscFunctionReturn(PETSC_SUCCESS);
6631   }
6632   /* Get points */
6633   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6634   /* Get sizes */
6635   asize = 0;
6636   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6637     PetscInt dof;
6638     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6639     asize += dof;
6640   }
6641   if (values) {
6642     const PetscScalar *vArray;
6643     PetscInt           size;
6644 
6645     if (*values) {
6646       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);
6647     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6648     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6649     PetscCall(VecGetArrayRead(v, &vArray));
6650     /* Get values */
6651     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6652     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6653     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6654     /* Cleanup array */
6655     PetscCall(VecRestoreArrayRead(v, &vArray));
6656   }
6657   if (csize) *csize = asize;
6658   /* Cleanup points */
6659   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6660   PetscFunctionReturn(PETSC_SUCCESS);
6661 }
6662 
6663 /*@C
6664   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6665 
6666   Not collective
6667 
6668   Input Parameters:
6669 + dm      - The `DM`
6670 . section - The section describing the layout in `v`, or `NULL` to use the default section
6671 . v       - The local vector
6672 - point   - The point in the `DM`
6673 
6674   Input/Output Parameters:
6675 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6676 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6677            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6678 
6679   Level: intermediate
6680 
6681   Notes:
6682   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6683   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6684   assembly function, and a user may already have allocated storage for this operation.
6685 
6686   A typical use could be
6687 .vb
6688    values = NULL;
6689    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6690    for (cl = 0; cl < clSize; ++cl) {
6691      <Compute on closure>
6692    }
6693    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6694 .ve
6695   or
6696 .vb
6697    PetscMalloc1(clMaxSize, &values);
6698    for (p = pStart; p < pEnd; ++p) {
6699      clSize = clMaxSize;
6700      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6701      for (cl = 0; cl < clSize; ++cl) {
6702        <Compute on closure>
6703      }
6704    }
6705    PetscFree(values);
6706 .ve
6707 
6708   Fortran Notes:
6709   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6710   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6711 
6712   `values` must be declared with
6713 .vb
6714   PetscScalar,dimension(:),pointer   :: values
6715 .ve
6716   and it will be allocated internally by PETSc to hold the values returned
6717 
6718 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6719 @*/
6720 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6721 {
6722   PetscFunctionBeginHot;
6723   PetscCall(DMPlexVecGetOrientedClosure(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6724   PetscFunctionReturn(PETSC_SUCCESS);
6725 }
6726 
6727 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6728 {
6729   DMLabel            depthLabel;
6730   PetscSection       clSection;
6731   IS                 clPoints;
6732   PetscScalar       *array;
6733   const PetscScalar *vArray;
6734   PetscInt          *points = NULL;
6735   const PetscInt    *clp, *perm = NULL;
6736   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6737 
6738   PetscFunctionBeginHot;
6739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6740   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6741   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6742   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6743   PetscCall(DMPlexGetDepth(dm, &mdepth));
6744   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6745   PetscCall(PetscSectionGetNumFields(section, &numFields));
6746   if (mdepth == 1 && numFields < 2) {
6747     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6748     PetscFunctionReturn(PETSC_SUCCESS);
6749   }
6750   /* Get points */
6751   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6752   for (clsize = 0, p = 0; p < Np; p++) {
6753     PetscInt dof;
6754     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6755     clsize += dof;
6756   }
6757   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6758   /* Filter points */
6759   for (p = 0; p < numPoints * 2; p += 2) {
6760     PetscInt dep;
6761 
6762     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6763     if (dep != depth) continue;
6764     points[Np * 2 + 0] = points[p];
6765     points[Np * 2 + 1] = points[p + 1];
6766     ++Np;
6767   }
6768   /* Get array */
6769   if (!values || !*values) {
6770     PetscInt asize = 0, dof;
6771 
6772     for (p = 0; p < Np * 2; p += 2) {
6773       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6774       asize += dof;
6775     }
6776     if (!values) {
6777       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6778       if (csize) *csize = asize;
6779       PetscFunctionReturn(PETSC_SUCCESS);
6780     }
6781     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6782   } else {
6783     array = *values;
6784   }
6785   PetscCall(VecGetArrayRead(v, &vArray));
6786   /* Get values */
6787   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6788   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6789   /* Cleanup points */
6790   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6791   /* Cleanup array */
6792   PetscCall(VecRestoreArrayRead(v, &vArray));
6793   if (!*values) {
6794     if (csize) *csize = size;
6795     *values = array;
6796   } else {
6797     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6798     *csize = size;
6799   }
6800   PetscFunctionReturn(PETSC_SUCCESS);
6801 }
6802 
6803 /*@C
6804   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6805 
6806   Not collective
6807 
6808   Input Parameters:
6809 + dm      - The `DM`
6810 . section - The section describing the layout in `v`, or `NULL` to use the default section
6811 . v       - The local vector
6812 . point   - The point in the `DM`
6813 . csize   - The number of values in the closure, or `NULL`
6814 - values  - The array of values
6815 
6816   Level: intermediate
6817 
6818   Note:
6819   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6820 
6821   Fortran Note:
6822   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6823   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6824 
6825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6826 @*/
6827 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6828 {
6829   PetscInt size = 0;
6830 
6831   PetscFunctionBegin;
6832   /* Should work without recalculating size */
6833   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6834   *values = NULL;
6835   PetscFunctionReturn(PETSC_SUCCESS);
6836 }
6837 
6838 static inline void add(PetscScalar *x, PetscScalar y)
6839 {
6840   *x += y;
6841 }
6842 static inline void insert(PetscScalar *x, PetscScalar y)
6843 {
6844   *x = y;
6845 }
6846 
6847 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[])
6848 {
6849   PetscInt        cdof;  /* The number of constraints on this point */
6850   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6851   PetscScalar    *a;
6852   PetscInt        off, cind = 0, k;
6853 
6854   PetscFunctionBegin;
6855   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6856   PetscCall(PetscSectionGetOffset(section, point, &off));
6857   a = &array[off];
6858   if (!cdof || setBC) {
6859     if (clperm) {
6860       if (perm) {
6861         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6862       } else {
6863         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6864       }
6865     } else {
6866       if (perm) {
6867         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6868       } else {
6869         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6870       }
6871     }
6872   } else {
6873     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6874     if (clperm) {
6875       if (perm) {
6876         for (k = 0; k < dof; ++k) {
6877           if ((cind < cdof) && (k == cdofs[cind])) {
6878             ++cind;
6879             continue;
6880           }
6881           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6882         }
6883       } else {
6884         for (k = 0; k < dof; ++k) {
6885           if ((cind < cdof) && (k == cdofs[cind])) {
6886             ++cind;
6887             continue;
6888           }
6889           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6890         }
6891       }
6892     } else {
6893       if (perm) {
6894         for (k = 0; k < dof; ++k) {
6895           if ((cind < cdof) && (k == cdofs[cind])) {
6896             ++cind;
6897             continue;
6898           }
6899           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6900         }
6901       } else {
6902         for (k = 0; k < dof; ++k) {
6903           if ((cind < cdof) && (k == cdofs[cind])) {
6904             ++cind;
6905             continue;
6906           }
6907           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6908         }
6909       }
6910     }
6911   }
6912   PetscFunctionReturn(PETSC_SUCCESS);
6913 }
6914 
6915 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[])
6916 {
6917   PetscInt        cdof;  /* The number of constraints on this point */
6918   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6919   PetscScalar    *a;
6920   PetscInt        off, cind = 0, k;
6921 
6922   PetscFunctionBegin;
6923   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6924   PetscCall(PetscSectionGetOffset(section, point, &off));
6925   a = &array[off];
6926   if (cdof) {
6927     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6928     if (clperm) {
6929       if (perm) {
6930         for (k = 0; k < dof; ++k) {
6931           if ((cind < cdof) && (k == cdofs[cind])) {
6932             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6933             cind++;
6934           }
6935         }
6936       } else {
6937         for (k = 0; k < dof; ++k) {
6938           if ((cind < cdof) && (k == cdofs[cind])) {
6939             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6940             cind++;
6941           }
6942         }
6943       }
6944     } else {
6945       if (perm) {
6946         for (k = 0; k < dof; ++k) {
6947           if ((cind < cdof) && (k == cdofs[cind])) {
6948             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6949             cind++;
6950           }
6951         }
6952       } else {
6953         for (k = 0; k < dof; ++k) {
6954           if ((cind < cdof) && (k == cdofs[cind])) {
6955             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6956             cind++;
6957           }
6958         }
6959       }
6960     }
6961   }
6962   PetscFunctionReturn(PETSC_SUCCESS);
6963 }
6964 
6965 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[])
6966 {
6967   PetscScalar    *a;
6968   PetscInt        fdof, foff, fcdof, foffset = *offset;
6969   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6970   PetscInt        cind = 0, b;
6971 
6972   PetscFunctionBegin;
6973   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6974   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6975   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6976   a = &array[foff];
6977   if (!fcdof || setBC) {
6978     if (clperm) {
6979       if (perm) {
6980         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6981       } else {
6982         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6983       }
6984     } else {
6985       if (perm) {
6986         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6987       } else {
6988         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6989       }
6990     }
6991   } else {
6992     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6993     if (clperm) {
6994       if (perm) {
6995         for (b = 0; b < fdof; b++) {
6996           if ((cind < fcdof) && (b == fcdofs[cind])) {
6997             ++cind;
6998             continue;
6999           }
7000           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7001         }
7002       } else {
7003         for (b = 0; b < fdof; b++) {
7004           if ((cind < fcdof) && (b == fcdofs[cind])) {
7005             ++cind;
7006             continue;
7007           }
7008           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7009         }
7010       }
7011     } else {
7012       if (perm) {
7013         for (b = 0; b < fdof; b++) {
7014           if ((cind < fcdof) && (b == fcdofs[cind])) {
7015             ++cind;
7016             continue;
7017           }
7018           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7019         }
7020       } else {
7021         for (b = 0; b < fdof; b++) {
7022           if ((cind < fcdof) && (b == fcdofs[cind])) {
7023             ++cind;
7024             continue;
7025           }
7026           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7027         }
7028       }
7029     }
7030   }
7031   *offset += fdof;
7032   PetscFunctionReturn(PETSC_SUCCESS);
7033 }
7034 
7035 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[])
7036 {
7037   PetscScalar    *a;
7038   PetscInt        fdof, foff, fcdof, foffset = *offset;
7039   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7040   PetscInt        Nc, cind = 0, ncind = 0, b;
7041   PetscBool       ncSet, fcSet;
7042 
7043   PetscFunctionBegin;
7044   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
7045   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7046   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
7047   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
7048   a = &array[foff];
7049   if (fcdof) {
7050     /* We just override fcdof and fcdofs with Ncc and comps */
7051     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7052     if (clperm) {
7053       if (perm) {
7054         if (comps) {
7055           for (b = 0; b < fdof; b++) {
7056             ncSet = fcSet = PETSC_FALSE;
7057             if (b % Nc == comps[ncind]) {
7058               ncind = (ncind + 1) % Ncc;
7059               ncSet = PETSC_TRUE;
7060             }
7061             if ((cind < fcdof) && (b == fcdofs[cind])) {
7062               ++cind;
7063               fcSet = PETSC_TRUE;
7064             }
7065             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7066           }
7067         } else {
7068           for (b = 0; b < fdof; b++) {
7069             if ((cind < fcdof) && (b == fcdofs[cind])) {
7070               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7071               ++cind;
7072             }
7073           }
7074         }
7075       } else {
7076         if (comps) {
7077           for (b = 0; b < fdof; b++) {
7078             ncSet = fcSet = PETSC_FALSE;
7079             if (b % Nc == comps[ncind]) {
7080               ncind = (ncind + 1) % Ncc;
7081               ncSet = PETSC_TRUE;
7082             }
7083             if ((cind < fcdof) && (b == fcdofs[cind])) {
7084               ++cind;
7085               fcSet = PETSC_TRUE;
7086             }
7087             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7088           }
7089         } else {
7090           for (b = 0; b < fdof; b++) {
7091             if ((cind < fcdof) && (b == fcdofs[cind])) {
7092               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7093               ++cind;
7094             }
7095           }
7096         }
7097       }
7098     } else {
7099       if (perm) {
7100         if (comps) {
7101           for (b = 0; b < fdof; b++) {
7102             ncSet = fcSet = PETSC_FALSE;
7103             if (b % Nc == comps[ncind]) {
7104               ncind = (ncind + 1) % Ncc;
7105               ncSet = PETSC_TRUE;
7106             }
7107             if ((cind < fcdof) && (b == fcdofs[cind])) {
7108               ++cind;
7109               fcSet = PETSC_TRUE;
7110             }
7111             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7112           }
7113         } else {
7114           for (b = 0; b < fdof; b++) {
7115             if ((cind < fcdof) && (b == fcdofs[cind])) {
7116               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7117               ++cind;
7118             }
7119           }
7120         }
7121       } else {
7122         if (comps) {
7123           for (b = 0; b < fdof; b++) {
7124             ncSet = fcSet = PETSC_FALSE;
7125             if (b % Nc == comps[ncind]) {
7126               ncind = (ncind + 1) % Ncc;
7127               ncSet = PETSC_TRUE;
7128             }
7129             if ((cind < fcdof) && (b == fcdofs[cind])) {
7130               ++cind;
7131               fcSet = PETSC_TRUE;
7132             }
7133             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7134           }
7135         } else {
7136           for (b = 0; b < fdof; b++) {
7137             if ((cind < fcdof) && (b == fcdofs[cind])) {
7138               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7139               ++cind;
7140             }
7141           }
7142         }
7143       }
7144     }
7145   }
7146   *offset += fdof;
7147   PetscFunctionReturn(PETSC_SUCCESS);
7148 }
7149 
7150 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7151 {
7152   PetscScalar    *array;
7153   const PetscInt *cone, *coneO;
7154   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7155 
7156   PetscFunctionBeginHot;
7157   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7158   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7159   PetscCall(DMPlexGetCone(dm, point, &cone));
7160   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7161   PetscCall(VecGetArray(v, &array));
7162   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7163     const PetscInt cp = !p ? point : cone[p - 1];
7164     const PetscInt o  = !p ? 0 : coneO[p - 1];
7165 
7166     if ((cp < pStart) || (cp >= pEnd)) {
7167       dof = 0;
7168       continue;
7169     }
7170     PetscCall(PetscSectionGetDof(section, cp, &dof));
7171     /* ADD_VALUES */
7172     {
7173       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7174       PetscScalar    *a;
7175       PetscInt        cdof, coff, cind = 0, k;
7176 
7177       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7178       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7179       a = &array[coff];
7180       if (!cdof) {
7181         if (o >= 0) {
7182           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7183         } else {
7184           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7185         }
7186       } else {
7187         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7188         if (o >= 0) {
7189           for (k = 0; k < dof; ++k) {
7190             if ((cind < cdof) && (k == cdofs[cind])) {
7191               ++cind;
7192               continue;
7193             }
7194             a[k] += values[off + k];
7195           }
7196         } else {
7197           for (k = 0; k < dof; ++k) {
7198             if ((cind < cdof) && (k == cdofs[cind])) {
7199               ++cind;
7200               continue;
7201             }
7202             a[k] += values[off + dof - k - 1];
7203           }
7204         }
7205       }
7206     }
7207   }
7208   PetscCall(VecRestoreArray(v, &array));
7209   PetscFunctionReturn(PETSC_SUCCESS);
7210 }
7211 
7212 /*@C
7213   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7214 
7215   Not collective
7216 
7217   Input Parameters:
7218 + dm      - The `DM`
7219 . section - The section describing the layout in `v`, or `NULL` to use the default section
7220 . v       - The local vector
7221 . point   - The point in the `DM`
7222 . values  - The array of values
7223 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7224             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7225 
7226   Level: intermediate
7227 
7228   Note:
7229   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7230 
7231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7232 @*/
7233 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7234 {
7235   PetscSection    clSection;
7236   IS              clPoints;
7237   PetscScalar    *array;
7238   PetscInt       *points = NULL;
7239   const PetscInt *clp, *clperm = NULL;
7240   PetscInt        depth, numFields, numPoints, p, clsize;
7241 
7242   PetscFunctionBeginHot;
7243   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7244   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7245   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7246   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7247   PetscCall(DMPlexGetDepth(dm, &depth));
7248   PetscCall(PetscSectionGetNumFields(section, &numFields));
7249   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7250     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7251     PetscFunctionReturn(PETSC_SUCCESS);
7252   }
7253   /* Get points */
7254   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7255   for (clsize = 0, p = 0; p < numPoints; p++) {
7256     PetscInt dof;
7257     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7258     clsize += dof;
7259   }
7260   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7261   /* Get array */
7262   PetscCall(VecGetArray(v, &array));
7263   /* Get values */
7264   if (numFields > 0) {
7265     PetscInt offset = 0, f;
7266     for (f = 0; f < numFields; ++f) {
7267       const PetscInt    **perms = NULL;
7268       const PetscScalar **flips = NULL;
7269 
7270       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7271       switch (mode) {
7272       case INSERT_VALUES:
7273         for (p = 0; p < numPoints; p++) {
7274           const PetscInt     point = points[2 * p];
7275           const PetscInt    *perm  = perms ? perms[p] : NULL;
7276           const PetscScalar *flip  = flips ? flips[p] : NULL;
7277           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7278         }
7279         break;
7280       case INSERT_ALL_VALUES:
7281         for (p = 0; p < numPoints; p++) {
7282           const PetscInt     point = points[2 * p];
7283           const PetscInt    *perm  = perms ? perms[p] : NULL;
7284           const PetscScalar *flip  = flips ? flips[p] : NULL;
7285           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7286         }
7287         break;
7288       case INSERT_BC_VALUES:
7289         for (p = 0; p < numPoints; p++) {
7290           const PetscInt     point = points[2 * p];
7291           const PetscInt    *perm  = perms ? perms[p] : NULL;
7292           const PetscScalar *flip  = flips ? flips[p] : NULL;
7293           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7294         }
7295         break;
7296       case ADD_VALUES:
7297         for (p = 0; p < numPoints; p++) {
7298           const PetscInt     point = points[2 * p];
7299           const PetscInt    *perm  = perms ? perms[p] : NULL;
7300           const PetscScalar *flip  = flips ? flips[p] : NULL;
7301           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7302         }
7303         break;
7304       case ADD_ALL_VALUES:
7305         for (p = 0; p < numPoints; p++) {
7306           const PetscInt     point = points[2 * p];
7307           const PetscInt    *perm  = perms ? perms[p] : NULL;
7308           const PetscScalar *flip  = flips ? flips[p] : NULL;
7309           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7310         }
7311         break;
7312       case ADD_BC_VALUES:
7313         for (p = 0; p < numPoints; p++) {
7314           const PetscInt     point = points[2 * p];
7315           const PetscInt    *perm  = perms ? perms[p] : NULL;
7316           const PetscScalar *flip  = flips ? flips[p] : NULL;
7317           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7318         }
7319         break;
7320       default:
7321         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7322       }
7323       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7324     }
7325   } else {
7326     PetscInt            dof, off;
7327     const PetscInt    **perms = NULL;
7328     const PetscScalar **flips = NULL;
7329 
7330     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7331     switch (mode) {
7332     case INSERT_VALUES:
7333       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7334         const PetscInt     point = points[2 * p];
7335         const PetscInt    *perm  = perms ? perms[p] : NULL;
7336         const PetscScalar *flip  = flips ? flips[p] : NULL;
7337         PetscCall(PetscSectionGetDof(section, point, &dof));
7338         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7339       }
7340       break;
7341     case INSERT_ALL_VALUES:
7342       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7343         const PetscInt     point = points[2 * p];
7344         const PetscInt    *perm  = perms ? perms[p] : NULL;
7345         const PetscScalar *flip  = flips ? flips[p] : NULL;
7346         PetscCall(PetscSectionGetDof(section, point, &dof));
7347         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7348       }
7349       break;
7350     case INSERT_BC_VALUES:
7351       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7352         const PetscInt     point = points[2 * p];
7353         const PetscInt    *perm  = perms ? perms[p] : NULL;
7354         const PetscScalar *flip  = flips ? flips[p] : NULL;
7355         PetscCall(PetscSectionGetDof(section, point, &dof));
7356         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7357       }
7358       break;
7359     case ADD_VALUES:
7360       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7361         const PetscInt     point = points[2 * p];
7362         const PetscInt    *perm  = perms ? perms[p] : NULL;
7363         const PetscScalar *flip  = flips ? flips[p] : NULL;
7364         PetscCall(PetscSectionGetDof(section, point, &dof));
7365         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7366       }
7367       break;
7368     case ADD_ALL_VALUES:
7369       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7370         const PetscInt     point = points[2 * p];
7371         const PetscInt    *perm  = perms ? perms[p] : NULL;
7372         const PetscScalar *flip  = flips ? flips[p] : NULL;
7373         PetscCall(PetscSectionGetDof(section, point, &dof));
7374         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7375       }
7376       break;
7377     case ADD_BC_VALUES:
7378       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7379         const PetscInt     point = points[2 * p];
7380         const PetscInt    *perm  = perms ? perms[p] : NULL;
7381         const PetscScalar *flip  = flips ? flips[p] : NULL;
7382         PetscCall(PetscSectionGetDof(section, point, &dof));
7383         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7384       }
7385       break;
7386     default:
7387       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7388     }
7389     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7390   }
7391   /* Cleanup points */
7392   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7393   /* Cleanup array */
7394   PetscCall(VecRestoreArray(v, &array));
7395   PetscFunctionReturn(PETSC_SUCCESS);
7396 }
7397 
7398 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7399 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7400 {
7401   PetscFunctionBegin;
7402   *contains = PETSC_TRUE;
7403   if (label) {
7404     PetscInt fdof;
7405 
7406     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7407     if (!*contains) {
7408       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7409       *offset += fdof;
7410       PetscFunctionReturn(PETSC_SUCCESS);
7411     }
7412   }
7413   PetscFunctionReturn(PETSC_SUCCESS);
7414 }
7415 
7416 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7417 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)
7418 {
7419   PetscSection    clSection;
7420   IS              clPoints;
7421   PetscScalar    *array;
7422   PetscInt       *points = NULL;
7423   const PetscInt *clp;
7424   PetscInt        numFields, numPoints, p;
7425   PetscInt        offset = 0, f;
7426 
7427   PetscFunctionBeginHot;
7428   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7429   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7430   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7431   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7432   PetscCall(PetscSectionGetNumFields(section, &numFields));
7433   /* Get points */
7434   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7435   /* Get array */
7436   PetscCall(VecGetArray(v, &array));
7437   /* Get values */
7438   for (f = 0; f < numFields; ++f) {
7439     const PetscInt    **perms = NULL;
7440     const PetscScalar **flips = NULL;
7441     PetscBool           contains;
7442 
7443     if (!fieldActive[f]) {
7444       for (p = 0; p < numPoints * 2; p += 2) {
7445         PetscInt fdof;
7446         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7447         offset += fdof;
7448       }
7449       continue;
7450     }
7451     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7452     switch (mode) {
7453     case INSERT_VALUES:
7454       for (p = 0; p < numPoints; p++) {
7455         const PetscInt     point = points[2 * p];
7456         const PetscInt    *perm  = perms ? perms[p] : NULL;
7457         const PetscScalar *flip  = flips ? flips[p] : NULL;
7458         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7459         if (!contains) continue;
7460         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7461       }
7462       break;
7463     case INSERT_ALL_VALUES:
7464       for (p = 0; p < numPoints; p++) {
7465         const PetscInt     point = points[2 * p];
7466         const PetscInt    *perm  = perms ? perms[p] : NULL;
7467         const PetscScalar *flip  = flips ? flips[p] : NULL;
7468         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7469         if (!contains) continue;
7470         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7471       }
7472       break;
7473     case INSERT_BC_VALUES:
7474       for (p = 0; p < numPoints; p++) {
7475         const PetscInt     point = points[2 * p];
7476         const PetscInt    *perm  = perms ? perms[p] : NULL;
7477         const PetscScalar *flip  = flips ? flips[p] : NULL;
7478         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7479         if (!contains) continue;
7480         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7481       }
7482       break;
7483     case ADD_VALUES:
7484       for (p = 0; p < numPoints; p++) {
7485         const PetscInt     point = points[2 * p];
7486         const PetscInt    *perm  = perms ? perms[p] : NULL;
7487         const PetscScalar *flip  = flips ? flips[p] : NULL;
7488         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7489         if (!contains) continue;
7490         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7491       }
7492       break;
7493     case ADD_ALL_VALUES:
7494       for (p = 0; p < numPoints; p++) {
7495         const PetscInt     point = points[2 * p];
7496         const PetscInt    *perm  = perms ? perms[p] : NULL;
7497         const PetscScalar *flip  = flips ? flips[p] : NULL;
7498         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7499         if (!contains) continue;
7500         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7501       }
7502       break;
7503     default:
7504       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7505     }
7506     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7507   }
7508   /* Cleanup points */
7509   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7510   /* Cleanup array */
7511   PetscCall(VecRestoreArray(v, &array));
7512   PetscFunctionReturn(PETSC_SUCCESS);
7513 }
7514 
7515 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7516 {
7517   PetscMPIInt rank;
7518   PetscInt    i, j;
7519 
7520   PetscFunctionBegin;
7521   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7522   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7523   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7524   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7525   numCIndices = numCIndices ? numCIndices : numRIndices;
7526   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7527   for (i = 0; i < numRIndices; i++) {
7528     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7529     for (j = 0; j < numCIndices; j++) {
7530 #if defined(PETSC_USE_COMPLEX)
7531       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7532 #else
7533       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7534 #endif
7535     }
7536     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7537   }
7538   PetscFunctionReturn(PETSC_SUCCESS);
7539 }
7540 
7541 /*
7542   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7543 
7544   Input Parameters:
7545 + section - The section for this data layout
7546 . islocal - Is the section (and thus indices being requested) local or global?
7547 . point   - The point contributing dofs with these indices
7548 . off     - The global offset of this point
7549 . loff    - The local offset of each field
7550 . setBC   - The flag determining whether to include indices of boundary values
7551 . perm    - A permutation of the dofs on this point, or NULL
7552 - indperm - A permutation of the entire indices array, or NULL
7553 
7554   Output Parameter:
7555 . indices - Indices for dofs on this point
7556 
7557   Level: developer
7558 
7559   Note: The indices could be local or global, depending on the value of 'off'.
7560 */
7561 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7562 {
7563   PetscInt        dof;   /* The number of unknowns on this point */
7564   PetscInt        cdof;  /* The number of constraints on this point */
7565   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7566   PetscInt        cind = 0, k;
7567 
7568   PetscFunctionBegin;
7569   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7570   PetscCall(PetscSectionGetDof(section, point, &dof));
7571   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7572   if (!cdof || setBC) {
7573     for (k = 0; k < dof; ++k) {
7574       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7575       const PetscInt ind    = indperm ? indperm[preind] : preind;
7576 
7577       indices[ind] = off + k;
7578     }
7579   } else {
7580     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7581     for (k = 0; k < dof; ++k) {
7582       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7583       const PetscInt ind    = indperm ? indperm[preind] : preind;
7584 
7585       if ((cind < cdof) && (k == cdofs[cind])) {
7586         /* Insert check for returning constrained indices */
7587         indices[ind] = -(off + k + 1);
7588         ++cind;
7589       } else {
7590         indices[ind] = off + k - (islocal ? 0 : cind);
7591       }
7592     }
7593   }
7594   *loff += dof;
7595   PetscFunctionReturn(PETSC_SUCCESS);
7596 }
7597 
7598 /*
7599  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7600 
7601  Input Parameters:
7602 + section - a section (global or local)
7603 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7604 . point - point within section
7605 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7606 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7607 . setBC - identify constrained (boundary condition) points via involution.
7608 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7609 . permsoff - offset
7610 - indperm - index permutation
7611 
7612  Output Parameter:
7613 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7614 . indices - array to hold indices (as defined by section) of each dof associated with point
7615 
7616  Notes:
7617  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7618  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7619  in the local vector.
7620 
7621  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7622  significant).  It is invalid to call with a global section and setBC=true.
7623 
7624  Developer Note:
7625  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7626  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7627  offset could be obtained from the section instead of passing it explicitly as we do now.
7628 
7629  Example:
7630  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7631  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7632  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7633  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.
7634 
7635  Level: developer
7636 */
7637 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[])
7638 {
7639   PetscInt numFields, foff, f;
7640 
7641   PetscFunctionBegin;
7642   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7643   PetscCall(PetscSectionGetNumFields(section, &numFields));
7644   for (f = 0, foff = 0; f < numFields; ++f) {
7645     PetscInt        fdof, cfdof;
7646     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7647     PetscInt        cind = 0, b;
7648     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7649 
7650     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7651     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7652     if (!cfdof || setBC) {
7653       for (b = 0; b < fdof; ++b) {
7654         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7655         const PetscInt ind    = indperm ? indperm[preind] : preind;
7656 
7657         indices[ind] = off + foff + b;
7658       }
7659     } else {
7660       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7661       for (b = 0; b < fdof; ++b) {
7662         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7663         const PetscInt ind    = indperm ? indperm[preind] : preind;
7664 
7665         if ((cind < cfdof) && (b == fcdofs[cind])) {
7666           indices[ind] = -(off + foff + b + 1);
7667           ++cind;
7668         } else {
7669           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7670         }
7671       }
7672     }
7673     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7674     foffs[f] += fdof;
7675   }
7676   PetscFunctionReturn(PETSC_SUCCESS);
7677 }
7678 
7679 /*
7680   This version believes the globalSection offsets for each field, rather than just the point offset
7681 
7682  . foffs - The offset into 'indices' for each field, since it is segregated by field
7683 
7684  Notes:
7685  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7686  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7687 */
7688 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7689 {
7690   PetscInt numFields, foff, f;
7691 
7692   PetscFunctionBegin;
7693   PetscCall(PetscSectionGetNumFields(section, &numFields));
7694   for (f = 0; f < numFields; ++f) {
7695     PetscInt        fdof, cfdof;
7696     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7697     PetscInt        cind = 0, b;
7698     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7699 
7700     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7701     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7702     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7703     if (!cfdof) {
7704       for (b = 0; b < fdof; ++b) {
7705         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7706         const PetscInt ind    = indperm ? indperm[preind] : preind;
7707 
7708         indices[ind] = foff + b;
7709       }
7710     } else {
7711       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7712       for (b = 0; b < fdof; ++b) {
7713         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7714         const PetscInt ind    = indperm ? indperm[preind] : preind;
7715 
7716         if ((cind < cfdof) && (b == fcdofs[cind])) {
7717           indices[ind] = -(foff + b + 1);
7718           ++cind;
7719         } else {
7720           indices[ind] = foff + b - cind;
7721         }
7722       }
7723     }
7724     foffs[f] += fdof;
7725   }
7726   PetscFunctionReturn(PETSC_SUCCESS);
7727 }
7728 
7729 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7730 {
7731   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7732 
7733   PetscFunctionBegin;
7734   PetscCall(PetscSectionGetNumFields(section, &numFields));
7735   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7736   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7737   for (PetscInt p = 0; p < nPoints; p++) {
7738     PetscInt     b       = pnts[2 * p];
7739     PetscInt     bSecDof = 0, bOff;
7740     PetscInt     cSecDof = 0;
7741     PetscSection indices_section;
7742 
7743     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7744     if (!bSecDof) continue;
7745     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7746     indices_section = cSecDof > 0 ? cSec : section;
7747     if (numFields) {
7748       PetscInt fStart[32], fEnd[32];
7749 
7750       fStart[0] = 0;
7751       fEnd[0]   = 0;
7752       for (PetscInt f = 0; f < numFields; f++) {
7753         PetscInt fDof = 0;
7754 
7755         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7756         fStart[f + 1] = fStart[f] + fDof;
7757         fEnd[f + 1]   = fStart[f + 1];
7758       }
7759       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7760       // only apply permutations on one side
7761       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7762       for (PetscInt f = 0; f < numFields; f++) {
7763         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7764       }
7765     } else {
7766       PetscInt bEnd = 0;
7767 
7768       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7769       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7770 
7771       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7772     }
7773   }
7774   PetscFunctionReturn(PETSC_SUCCESS);
7775 }
7776 
7777 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[])
7778 {
7779   Mat             cMat;
7780   PetscSection    aSec, cSec;
7781   IS              aIS;
7782   PetscInt        aStart = -1, aEnd = -1;
7783   PetscInt        sStart = -1, sEnd = -1;
7784   PetscInt        cStart = -1, cEnd = -1;
7785   const PetscInt *anchors;
7786   PetscInt        numFields, p;
7787   PetscInt        newNumPoints = 0, newNumIndices = 0;
7788   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7789   PetscInt        oldOffsets[32];
7790   PetscInt        newOffsets[32];
7791   PetscInt        oldOffsetsCopy[32];
7792   PetscInt        newOffsetsCopy[32];
7793   PetscScalar    *modMat         = NULL;
7794   PetscBool       anyConstrained = PETSC_FALSE;
7795 
7796   PetscFunctionBegin;
7797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7798   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7799   PetscCall(PetscSectionGetNumFields(section, &numFields));
7800 
7801   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7802   /* if there are point-to-point constraints */
7803   if (aSec) {
7804     PetscCall(PetscArrayzero(newOffsets, 32));
7805     PetscCall(PetscArrayzero(oldOffsets, 32));
7806     PetscCall(ISGetIndices(aIS, &anchors));
7807     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7808     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7809     /* figure out how many points are going to be in the new element matrix
7810      * (we allow double counting, because it's all just going to be summed
7811      * into the global matrix anyway) */
7812     for (p = 0; p < 2 * numPoints; p += 2) {
7813       PetscInt b    = points[p];
7814       PetscInt bDof = 0, bSecDof = 0;
7815 
7816       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7817       if (!bSecDof) continue;
7818 
7819       for (PetscInt f = 0; f < numFields; f++) {
7820         PetscInt fDof = 0;
7821 
7822         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7823         oldOffsets[f + 1] += fDof;
7824       }
7825       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7826       if (bDof) {
7827         /* this point is constrained */
7828         /* it is going to be replaced by its anchors */
7829         PetscInt bOff, q;
7830 
7831         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7832         for (q = 0; q < bDof; q++) {
7833           PetscInt a    = anchors[bOff + q];
7834           PetscInt aDof = 0;
7835 
7836           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7837           if (aDof) {
7838             anyConstrained = PETSC_TRUE;
7839             newNumPoints += 1;
7840           }
7841           newNumIndices += aDof;
7842           for (PetscInt f = 0; f < numFields; ++f) {
7843             PetscInt fDof = 0;
7844 
7845             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7846             newOffsets[f + 1] += fDof;
7847           }
7848         }
7849       } else {
7850         /* this point is not constrained */
7851         newNumPoints++;
7852         newNumIndices += bSecDof;
7853         for (PetscInt f = 0; f < numFields; ++f) {
7854           PetscInt fDof;
7855 
7856           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7857           newOffsets[f + 1] += fDof;
7858         }
7859       }
7860     }
7861   }
7862   if (!anyConstrained) {
7863     if (outNumPoints) *outNumPoints = 0;
7864     if (outNumIndices) *outNumIndices = 0;
7865     if (outPoints) *outPoints = NULL;
7866     if (outMat) *outMat = NULL;
7867     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7868     PetscFunctionReturn(PETSC_SUCCESS);
7869   }
7870 
7871   if (outNumPoints) *outNumPoints = newNumPoints;
7872   if (outNumIndices) *outNumIndices = newNumIndices;
7873 
7874   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7875   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7876 
7877   if (!outPoints && !outMat) {
7878     if (offsets) {
7879       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7880     }
7881     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7882     PetscFunctionReturn(PETSC_SUCCESS);
7883   }
7884 
7885   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7886   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7887 
7888   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7889   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7890 
7891   /* output arrays */
7892   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7893   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7894 
7895   // get the new Points
7896   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7897     PetscInt b    = points[2 * p];
7898     PetscInt bDof = 0, bSecDof = 0, bOff;
7899 
7900     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7901     if (!bSecDof) continue;
7902     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7903     if (bDof) {
7904       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7905       for (PetscInt q = 0; q < bDof; q++) {
7906         PetscInt a = anchors[bOff + q], aDof = 0;
7907 
7908         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7909         if (aDof) {
7910           newPoints[2 * newP]     = a;
7911           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7912           newP++;
7913         }
7914       }
7915     } else {
7916       newPoints[2 * newP]     = b;
7917       newPoints[2 * newP + 1] = points[2 * p + 1];
7918       newP++;
7919     }
7920   }
7921 
7922   if (outMat) {
7923     PetscScalar *tmpMat;
7924     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7925     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7926 
7927     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7928     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7929     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7930     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7931 
7932     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7933     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7934 
7935     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7936     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7937 
7938     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7939     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7940     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7941     // for each field, insert the anchor modification into modMat
7942     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7943       PetscInt fStart    = oldOffsets[f];
7944       PetscInt fNewStart = newOffsets[f];
7945       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7946         PetscInt b    = points[2 * p];
7947         PetscInt bDof = 0, bSecDof = 0, bOff;
7948 
7949         if (b >= sStart && b < sEnd) {
7950           if (numFields) {
7951             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7952           } else {
7953             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7954           }
7955         }
7956         if (!bSecDof) continue;
7957         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7958         if (bDof) {
7959           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7960           for (PetscInt q = 0; q < bDof; q++, newP++) {
7961             PetscInt a = anchors[bOff + q], aDof = 0;
7962 
7963             if (a >= sStart && a < sEnd) {
7964               if (numFields) {
7965                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7966               } else {
7967                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7968               }
7969             }
7970             if (aDof) {
7971               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7972               for (PetscInt d = 0; d < bSecDof; d++) {
7973                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7974               }
7975             }
7976             oNew += aDof;
7977           }
7978         } else {
7979           // Insert the identity matrix in this block
7980           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7981           oNew += bSecDof;
7982           newP++;
7983         }
7984         o += bSecDof;
7985       }
7986     }
7987 
7988     *outMat = modMat;
7989 
7990     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7991     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7992     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7993     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7994     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7995   }
7996   PetscCall(ISRestoreIndices(aIS, &anchors));
7997 
7998   /* output */
7999   if (outPoints) {
8000     *outPoints = newPoints;
8001   } else {
8002     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
8003   }
8004   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
8005   PetscFunctionReturn(PETSC_SUCCESS);
8006 }
8007 
8008 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)
8009 {
8010   PetscScalar *modMat        = NULL;
8011   PetscInt     newNumIndices = -1;
8012 
8013   PetscFunctionBegin;
8014   /* 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.
8015      modMat is that matrix C */
8016   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
8017   if (outNumIndices) *outNumIndices = newNumIndices;
8018   if (modMat) {
8019     const PetscScalar *newValues = values;
8020 
8021     if (multiplyRight) {
8022       PetscScalar *newNewValues = NULL;
8023       PetscBLASInt M, N, K;
8024       PetscScalar  a = 1.0, b = 0.0;
8025 
8026       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);
8027 
8028       PetscCall(PetscBLASIntCast(newNumIndices, &M));
8029       PetscCall(PetscBLASIntCast(numRows, &N));
8030       PetscCall(PetscBLASIntCast(numIndices, &K));
8031       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
8032       // row-major to column-major conversion, right multiplication becomes left multiplication
8033       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
8034       numCols   = newNumIndices;
8035       newValues = newNewValues;
8036     }
8037 
8038     if (multiplyLeft) {
8039       PetscScalar *newNewValues = NULL;
8040       PetscBLASInt M, N, K;
8041       PetscScalar  a = 1.0, b = 0.0;
8042 
8043       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);
8044 
8045       PetscCall(PetscBLASIntCast(numCols, &M));
8046       PetscCall(PetscBLASIntCast(newNumIndices, &N));
8047       PetscCall(PetscBLASIntCast(numIndices, &K));
8048       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
8049       // row-major to column-major conversion, left multiplication becomes right multiplication
8050       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
8051       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
8052       newValues = newNewValues;
8053     }
8054     *outValues = (PetscScalar *)newValues;
8055     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
8056   }
8057   PetscFunctionReturn(PETSC_SUCCESS);
8058 }
8059 
8060 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)
8061 {
8062   PetscFunctionBegin;
8063   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8064   PetscFunctionReturn(PETSC_SUCCESS);
8065 }
8066 
8067 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8068 {
8069   /* Closure ordering */
8070   PetscSection    clSection;
8071   IS              clPoints;
8072   const PetscInt *clp;
8073   PetscInt       *points;
8074   PetscInt        Ncl, Ni = 0;
8075 
8076   PetscFunctionBeginHot;
8077   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8078   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8079     PetscInt dof;
8080 
8081     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8082     Ni += dof;
8083   }
8084   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8085   *closureSize = Ni;
8086   PetscFunctionReturn(PETSC_SUCCESS);
8087 }
8088 
8089 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)
8090 {
8091   /* Closure ordering */
8092   PetscSection    clSection;
8093   IS              clPoints;
8094   const PetscInt *clp;
8095   PetscInt       *points;
8096   const PetscInt *clperm = NULL;
8097   /* Dof permutation and sign flips */
8098   const PetscInt    **perms[32] = {NULL};
8099   const PetscScalar **flips[32] = {NULL};
8100   PetscScalar        *valCopy   = NULL;
8101   /* Hanging node constraints */
8102   PetscInt    *pointsC = NULL;
8103   PetscScalar *valuesC = NULL;
8104   PetscInt     NclC, NiC;
8105 
8106   PetscInt *idx;
8107   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8108   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8109   PetscInt  idxStart, idxEnd;
8110   PetscInt  nRows, nCols;
8111 
8112   PetscFunctionBeginHot;
8113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8114   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8115   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8116   PetscAssertPointer(numRows, 6);
8117   PetscAssertPointer(numCols, 7);
8118   if (indices) PetscAssertPointer(indices, 8);
8119   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8120   if (values) PetscAssertPointer(values, 10);
8121   PetscCall(PetscSectionGetNumFields(section, &Nf));
8122   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8123   PetscCall(PetscArrayzero(offsets, 32));
8124   /* 1) Get points in closure */
8125   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8126   if (useClPerm) {
8127     PetscInt depth, clsize;
8128     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8129     for (clsize = 0, p = 0; p < Ncl; p++) {
8130       PetscInt dof;
8131       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8132       clsize += dof;
8133     }
8134     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8135   }
8136   /* 2) Get number of indices on these points and field offsets from section */
8137   for (p = 0; p < Ncl * 2; p += 2) {
8138     PetscInt dof, fdof;
8139 
8140     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8141     for (f = 0; f < Nf; ++f) {
8142       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8143       offsets[f + 1] += fdof;
8144     }
8145     Ni += dof;
8146   }
8147   if (*numRows == -1) *numRows = Ni;
8148   if (*numCols == -1) *numCols = Ni;
8149   nRows = *numRows;
8150   nCols = *numCols;
8151   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8152   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8153   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8154   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8155   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8156   for (f = 0; f < PetscMax(1, Nf); ++f) {
8157     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8158     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8159     /* may need to apply sign changes to the element matrix */
8160     if (values && flips[f]) {
8161       PetscInt foffset = offsets[f];
8162 
8163       for (p = 0; p < Ncl; ++p) {
8164         PetscInt           pnt  = points[2 * p], fdof;
8165         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8166 
8167         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8168         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8169         if (flip) {
8170           PetscInt i, j, k;
8171 
8172           if (!valCopy) {
8173             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8174             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8175             *values = valCopy;
8176           }
8177           for (i = 0; i < fdof; ++i) {
8178             PetscScalar fval = flip[i];
8179 
8180             if (multiplyRight) {
8181               for (k = 0; k < nRows; ++k) valCopy[Ni * k + (foffset + i)] *= fval;
8182             }
8183             if (multiplyLeft) {
8184               for (k = 0; k < nCols; ++k) valCopy[nCols * (foffset + i) + k] *= fval;
8185             }
8186           }
8187         }
8188         foffset += fdof;
8189       }
8190     }
8191   }
8192   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8193   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8194   if (NclC) {
8195     if (multiplyRight) *numCols = NiC;
8196     if (multiplyLeft) *numRows = NiC;
8197     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8198     for (f = 0; f < PetscMax(1, Nf); ++f) {
8199       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8200       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8201     }
8202     for (f = 0; f < PetscMax(1, Nf); ++f) {
8203       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8204       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8205     }
8206     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8207     Ncl    = NclC;
8208     Ni     = NiC;
8209     points = pointsC;
8210     if (values) *values = valuesC;
8211   }
8212   /* 5) Calculate indices */
8213   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8214   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8215   if (Nf) {
8216     PetscInt  idxOff;
8217     PetscBool useFieldOffsets;
8218 
8219     if (outOffsets) {
8220       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8221     }
8222     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8223     if (useFieldOffsets) {
8224       for (p = 0; p < Ncl; ++p) {
8225         const PetscInt pnt = points[p * 2];
8226 
8227         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8228       }
8229     } else {
8230       for (p = 0; p < Ncl; ++p) {
8231         const PetscInt pnt = points[p * 2];
8232 
8233         if (pnt < idxStart || pnt >= idxEnd) continue;
8234         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8235         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8236          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8237          * global section. */
8238         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8239       }
8240     }
8241   } else {
8242     PetscInt off = 0, idxOff;
8243 
8244     for (p = 0; p < Ncl; ++p) {
8245       const PetscInt  pnt  = points[p * 2];
8246       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8247 
8248       if (pnt < idxStart || pnt >= idxEnd) continue;
8249       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8250       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8251        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8252       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8253     }
8254   }
8255   /* 6) Cleanup */
8256   for (f = 0; f < PetscMax(1, Nf); ++f) {
8257     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8258     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8259   }
8260   if (NclC) {
8261     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8262   } else {
8263     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8264   }
8265 
8266   if (indices) *indices = idx;
8267   PetscFunctionReturn(PETSC_SUCCESS);
8268 }
8269 
8270 /*@C
8271   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8272 
8273   Not collective
8274 
8275   Input Parameters:
8276 + dm         - The `DM`
8277 . section    - The `PetscSection` describing the points (a local section)
8278 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8279 . point      - The point defining the closure
8280 - useClPerm  - Use the closure point permutation if available
8281 
8282   Output Parameters:
8283 + numIndices - The number of dof indices in the closure of point with the input sections
8284 . indices    - The dof indices
8285 . outOffsets - Array to write the field offsets into, or `NULL`
8286 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8287 
8288   Level: advanced
8289 
8290   Notes:
8291   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8292 
8293   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8294   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8295   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8296   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8297   indices (with the above semantics) are implied.
8298 
8299 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8300           `PetscSection`, `DMGetGlobalSection()`
8301 @*/
8302 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8303 {
8304   PetscInt numRows = -1, numCols = -1;
8305 
8306   PetscFunctionBeginHot;
8307   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8308   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8309   *numIndices = numRows;
8310   PetscFunctionReturn(PETSC_SUCCESS);
8311 }
8312 
8313 /*@C
8314   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8315 
8316   Not collective
8317 
8318   Input Parameters:
8319 + dm         - The `DM`
8320 . section    - The `PetscSection` describing the points (a local section)
8321 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8322 . point      - The point defining the closure
8323 - useClPerm  - Use the closure point permutation if available
8324 
8325   Output Parameters:
8326 + numIndices - The number of dof indices in the closure of point with the input sections
8327 . indices    - The dof indices
8328 . outOffsets - Array to write the field offsets into, or `NULL`
8329 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8330 
8331   Level: advanced
8332 
8333   Notes:
8334   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8335 
8336   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8337   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8338   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8339   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8340   indices (with the above semantics) are implied.
8341 
8342 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8343 @*/
8344 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8345 {
8346   PetscFunctionBegin;
8347   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8348   PetscAssertPointer(indices, 7);
8349   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8350   PetscFunctionReturn(PETSC_SUCCESS);
8351 }
8352 
8353 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8354 {
8355   DM_Plex           *mesh = (DM_Plex *)dm->data;
8356   PetscInt          *indices;
8357   PetscInt           numIndices;
8358   const PetscScalar *valuesOrig = values;
8359   PetscErrorCode     ierr;
8360 
8361   PetscFunctionBegin;
8362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8363   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8364   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8365   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8366   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8367   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8368 
8369   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8370 
8371   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8372   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8373   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8374   if (ierr) {
8375     PetscMPIInt rank;
8376 
8377     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8378     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8379     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8380     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8381     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8382     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8383   }
8384   if (mesh->printFEM > 1) {
8385     PetscInt i;
8386     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8387     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8388     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8389   }
8390 
8391   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8392   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8393   PetscFunctionReturn(PETSC_SUCCESS);
8394 }
8395 
8396 /*@C
8397   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8398 
8399   Not collective
8400 
8401   Input Parameters:
8402 + dm            - The `DM`
8403 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8404 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8405 . A             - The matrix
8406 . point         - The point in the `DM`
8407 . values        - The array of values
8408 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8409 
8410   Level: intermediate
8411 
8412 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8413 @*/
8414 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8415 {
8416   PetscFunctionBegin;
8417   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8418   PetscFunctionReturn(PETSC_SUCCESS);
8419 }
8420 
8421 /*@C
8422   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8423 
8424   Not collective
8425 
8426   Input Parameters:
8427 + dmRow            - The `DM` for the row fields
8428 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8429 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8430 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8431 . dmCol            - The `DM` for the column fields
8432 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8433 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8434 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8435 . A                - The matrix
8436 . point            - The point in the `DM`
8437 . values           - The array of values
8438 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8439 
8440   Level: intermediate
8441 
8442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8443 @*/
8444 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)
8445 {
8446   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8447   PetscInt          *indicesRow, *indicesCol;
8448   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8449   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8450 
8451   PetscErrorCode ierr;
8452 
8453   PetscFunctionBegin;
8454   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8455   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8456   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8457   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8458   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8459   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8460   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8461   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8462   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8463   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8464   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8465 
8466   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8467   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8468   valuesV1 = valuesV0;
8469   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8470   valuesV2 = valuesV1;
8471   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8472 
8473   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8474   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8475   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8476   if (ierr) {
8477     PetscMPIInt rank;
8478 
8479     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8480     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8481     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8482     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8483     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8484     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8485     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8486   }
8487 
8488   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8489   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8490   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8491   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8492   PetscFunctionReturn(PETSC_SUCCESS);
8493 }
8494 
8495 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8496 {
8497   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8498   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8499   PetscInt       *cpoints = NULL;
8500   PetscInt       *findices, *cindices;
8501   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8502   PetscInt        foffsets[32], coffsets[32];
8503   DMPolytopeType  ct;
8504   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8505   PetscErrorCode  ierr;
8506 
8507   PetscFunctionBegin;
8508   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8509   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8510   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8511   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8512   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8513   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8514   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8515   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8516   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8517   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8518   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8519   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8520   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8521   PetscCall(PetscArrayzero(foffsets, 32));
8522   PetscCall(PetscArrayzero(coffsets, 32));
8523   /* Column indices */
8524   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8525   maxFPoints = numCPoints;
8526   /* Compress out points not in the section */
8527   /*   TODO: Squeeze out points with 0 dof as well */
8528   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8529   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8530     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8531       cpoints[q * 2]     = cpoints[p];
8532       cpoints[q * 2 + 1] = cpoints[p + 1];
8533       ++q;
8534     }
8535   }
8536   numCPoints = q;
8537   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8538     PetscInt fdof;
8539 
8540     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8541     if (!dof) continue;
8542     for (f = 0; f < numFields; ++f) {
8543       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8544       coffsets[f + 1] += fdof;
8545     }
8546     numCIndices += dof;
8547   }
8548   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8549   /* Row indices */
8550   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8551   {
8552     DMPlexTransform tr;
8553     DMPolytopeType *rct;
8554     PetscInt       *rsize, *rcone, *rornt, Nt;
8555 
8556     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8557     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8558     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8559     numSubcells = rsize[Nt - 1];
8560     PetscCall(DMPlexTransformDestroy(&tr));
8561   }
8562   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8563   for (r = 0, q = 0; r < numSubcells; ++r) {
8564     /* TODO Map from coarse to fine cells */
8565     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8566     /* Compress out points not in the section */
8567     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8568     for (p = 0; p < numFPoints * 2; p += 2) {
8569       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8570         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8571         if (!dof) continue;
8572         for (s = 0; s < q; ++s)
8573           if (fpoints[p] == ftotpoints[s * 2]) break;
8574         if (s < q) continue;
8575         ftotpoints[q * 2]     = fpoints[p];
8576         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8577         ++q;
8578       }
8579     }
8580     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8581   }
8582   numFPoints = q;
8583   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8584     PetscInt fdof;
8585 
8586     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8587     if (!dof) continue;
8588     for (f = 0; f < numFields; ++f) {
8589       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8590       foffsets[f + 1] += fdof;
8591     }
8592     numFIndices += dof;
8593   }
8594   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8595 
8596   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8597   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8598   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8599   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8600   if (numFields) {
8601     const PetscInt **permsF[32] = {NULL};
8602     const PetscInt **permsC[32] = {NULL};
8603 
8604     for (f = 0; f < numFields; f++) {
8605       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8606       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8607     }
8608     for (p = 0; p < numFPoints; p++) {
8609       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8610       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8611     }
8612     for (p = 0; p < numCPoints; p++) {
8613       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8614       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8615     }
8616     for (f = 0; f < numFields; f++) {
8617       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8618       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8619     }
8620   } else {
8621     const PetscInt **permsF = NULL;
8622     const PetscInt **permsC = NULL;
8623 
8624     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8625     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8626     for (p = 0, off = 0; p < numFPoints; p++) {
8627       const PetscInt *perm = permsF ? permsF[p] : NULL;
8628 
8629       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8630       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8631     }
8632     for (p = 0, off = 0; p < numCPoints; p++) {
8633       const PetscInt *perm = permsC ? permsC[p] : NULL;
8634 
8635       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8636       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8637     }
8638     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8639     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8640   }
8641   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8642   /* TODO: flips */
8643   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8644   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8645   if (ierr) {
8646     PetscMPIInt rank;
8647 
8648     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8649     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8650     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8651     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8652     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8653   }
8654   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8655   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8656   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8657   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8658   PetscFunctionReturn(PETSC_SUCCESS);
8659 }
8660 
8661 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8662 {
8663   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8664   PetscInt       *cpoints      = NULL;
8665   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8666   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8667   DMPolytopeType  ct;
8668   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8669 
8670   PetscFunctionBegin;
8671   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8672   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8673   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8674   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8675   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8676   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8677   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8678   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8679   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8680   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8681   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8682   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8683   /* Column indices */
8684   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8685   maxFPoints = numCPoints;
8686   /* Compress out points not in the section */
8687   /*   TODO: Squeeze out points with 0 dof as well */
8688   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8689   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8690     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8691       cpoints[q * 2]     = cpoints[p];
8692       cpoints[q * 2 + 1] = cpoints[p + 1];
8693       ++q;
8694     }
8695   }
8696   numCPoints = q;
8697   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8698     PetscInt fdof;
8699 
8700     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8701     if (!dof) continue;
8702     for (f = 0; f < numFields; ++f) {
8703       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8704       coffsets[f + 1] += fdof;
8705     }
8706     numCIndices += dof;
8707   }
8708   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8709   /* Row indices */
8710   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8711   {
8712     DMPlexTransform tr;
8713     DMPolytopeType *rct;
8714     PetscInt       *rsize, *rcone, *rornt, Nt;
8715 
8716     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8717     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8718     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8719     numSubcells = rsize[Nt - 1];
8720     PetscCall(DMPlexTransformDestroy(&tr));
8721   }
8722   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8723   for (r = 0, q = 0; r < numSubcells; ++r) {
8724     /* TODO Map from coarse to fine cells */
8725     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8726     /* Compress out points not in the section */
8727     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8728     for (p = 0; p < numFPoints * 2; p += 2) {
8729       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8730         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8731         if (!dof) continue;
8732         for (s = 0; s < q; ++s)
8733           if (fpoints[p] == ftotpoints[s * 2]) break;
8734         if (s < q) continue;
8735         ftotpoints[q * 2]     = fpoints[p];
8736         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8737         ++q;
8738       }
8739     }
8740     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8741   }
8742   numFPoints = q;
8743   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8744     PetscInt fdof;
8745 
8746     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8747     if (!dof) continue;
8748     for (f = 0; f < numFields; ++f) {
8749       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8750       foffsets[f + 1] += fdof;
8751     }
8752     numFIndices += dof;
8753   }
8754   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8755 
8756   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8757   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8758   if (numFields) {
8759     const PetscInt **permsF[32] = {NULL};
8760     const PetscInt **permsC[32] = {NULL};
8761 
8762     for (f = 0; f < numFields; f++) {
8763       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8764       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8765     }
8766     for (p = 0; p < numFPoints; p++) {
8767       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8768       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8769     }
8770     for (p = 0; p < numCPoints; p++) {
8771       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8772       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8773     }
8774     for (f = 0; f < numFields; f++) {
8775       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8776       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8777     }
8778   } else {
8779     const PetscInt **permsF = NULL;
8780     const PetscInt **permsC = NULL;
8781 
8782     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8783     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8784     for (p = 0, off = 0; p < numFPoints; p++) {
8785       const PetscInt *perm = permsF ? permsF[p] : NULL;
8786 
8787       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8788       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8789     }
8790     for (p = 0, off = 0; p < numCPoints; p++) {
8791       const PetscInt *perm = permsC ? permsC[p] : NULL;
8792 
8793       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8794       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8795     }
8796     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8797     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8798   }
8799   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8800   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8801   PetscFunctionReturn(PETSC_SUCCESS);
8802 }
8803 
8804 /*@
8805   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8806 
8807   Input Parameter:
8808 . dm - The `DMPLEX` object
8809 
8810   Output Parameter:
8811 . cellHeight - The height of a cell
8812 
8813   Level: developer
8814 
8815 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8816 @*/
8817 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8818 {
8819   DM_Plex *mesh = (DM_Plex *)dm->data;
8820 
8821   PetscFunctionBegin;
8822   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8823   PetscAssertPointer(cellHeight, 2);
8824   *cellHeight = mesh->vtkCellHeight;
8825   PetscFunctionReturn(PETSC_SUCCESS);
8826 }
8827 
8828 /*@
8829   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8830 
8831   Input Parameters:
8832 + dm         - The `DMPLEX` object
8833 - cellHeight - The height of a cell
8834 
8835   Level: developer
8836 
8837 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8838 @*/
8839 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8840 {
8841   DM_Plex *mesh = (DM_Plex *)dm->data;
8842 
8843   PetscFunctionBegin;
8844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8845   mesh->vtkCellHeight = cellHeight;
8846   PetscFunctionReturn(PETSC_SUCCESS);
8847 }
8848 
8849 /*@
8850   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8851 
8852   Input Parameters:
8853 + dm - The `DMPLEX` object
8854 - ct - The `DMPolytopeType` of the cell
8855 
8856   Output Parameters:
8857 + start - The first cell of this type, or `NULL`
8858 - end   - The upper bound on this celltype, or `NULL`
8859 
8860   Level: advanced
8861 
8862 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8863 @*/
8864 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8865 {
8866   DM_Plex *mesh = (DM_Plex *)dm->data;
8867   DMLabel  label;
8868   PetscInt pStart, pEnd;
8869 
8870   PetscFunctionBegin;
8871   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8872   if (start) {
8873     PetscAssertPointer(start, 3);
8874     *start = 0;
8875   }
8876   if (end) {
8877     PetscAssertPointer(end, 4);
8878     *end = 0;
8879   }
8880   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8881   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8882   if (mesh->tr) {
8883     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8884   } else {
8885     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8886     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8887     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8888   }
8889   PetscFunctionReturn(PETSC_SUCCESS);
8890 }
8891 
8892 /*@
8893   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8894 
8895   Input Parameters:
8896 + dm    - The `DMPLEX` object
8897 - depth - The depth for the given point stratum
8898 
8899   Output Parameter:
8900 . gsize - The global number of points in the stratum
8901 
8902   Level: advanced
8903 
8904 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8905 @*/
8906 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8907 {
8908   PetscSF         sf;
8909   const PetscInt *leaves;
8910   PetscInt        Nl, loc, start, end, lsize = 0;
8911 
8912   PetscFunctionBegin;
8913   PetscCall(DMGetPointSF(dm, &sf));
8914   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8915   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8916   for (PetscInt p = start; p < end; ++p) {
8917     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8918     if (loc < 0) ++lsize;
8919   }
8920   PetscCallMPI(MPIU_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8921   PetscFunctionReturn(PETSC_SUCCESS);
8922 }
8923 
8924 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8925 {
8926   PetscSection section, globalSection;
8927   PetscInt    *numbers, p;
8928 
8929   PetscFunctionBegin;
8930   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8931   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8932   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8933   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8934   PetscCall(PetscSectionSetUp(section));
8935   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8936   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8937   for (p = pStart; p < pEnd; ++p) {
8938     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8939     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8940     else numbers[p - pStart] += shift;
8941   }
8942   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8943   if (globalSize) {
8944     PetscLayout layout;
8945     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8946     PetscCall(PetscLayoutGetSize(layout, globalSize));
8947     PetscCall(PetscLayoutDestroy(&layout));
8948   }
8949   PetscCall(PetscSectionDestroy(&section));
8950   PetscCall(PetscSectionDestroy(&globalSection));
8951   PetscFunctionReturn(PETSC_SUCCESS);
8952 }
8953 
8954 /*@
8955   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8956 
8957   Input Parameters:
8958 + dm         - The `DMPLEX` object
8959 - includeAll - Whether to include all cells, or just the simplex and box cells
8960 
8961   Output Parameter:
8962 . globalCellNumbers - Global cell numbers for all cells on this process
8963 
8964   Level: developer
8965 
8966 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8967 @*/
8968 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8969 {
8970   PetscInt cellHeight, cStart, cEnd;
8971 
8972   PetscFunctionBegin;
8973   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8974   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8975   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8976   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8977   PetscFunctionReturn(PETSC_SUCCESS);
8978 }
8979 
8980 /*@
8981   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8982 
8983   Input Parameter:
8984 . dm - The `DMPLEX` object
8985 
8986   Output Parameter:
8987 . globalCellNumbers - Global cell numbers for all cells on this process
8988 
8989   Level: developer
8990 
8991 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8992 @*/
8993 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8994 {
8995   DM_Plex *mesh = (DM_Plex *)dm->data;
8996 
8997   PetscFunctionBegin;
8998   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8999   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
9000   *globalCellNumbers = mesh->globalCellNumbers;
9001   PetscFunctionReturn(PETSC_SUCCESS);
9002 }
9003 
9004 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
9005 {
9006   PetscInt vStart, vEnd;
9007 
9008   PetscFunctionBegin;
9009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9010   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9011   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
9012   PetscFunctionReturn(PETSC_SUCCESS);
9013 }
9014 
9015 /*@
9016   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
9017 
9018   Input Parameter:
9019 . dm - The `DMPLEX` object
9020 
9021   Output Parameter:
9022 . globalVertexNumbers - Global vertex numbers for all vertices on this process
9023 
9024   Level: developer
9025 
9026 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9027 @*/
9028 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
9029 {
9030   DM_Plex *mesh = (DM_Plex *)dm->data;
9031 
9032   PetscFunctionBegin;
9033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9034   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
9035   *globalVertexNumbers = mesh->globalVertexNumbers;
9036   PetscFunctionReturn(PETSC_SUCCESS);
9037 }
9038 
9039 /*@
9040   DMPlexCreatePointNumbering - Create a global numbering for all points.
9041 
9042   Collective
9043 
9044   Input Parameter:
9045 . dm - The `DMPLEX` object
9046 
9047   Output Parameter:
9048 . globalPointNumbers - Global numbers for all points on this process
9049 
9050   Level: developer
9051 
9052   Notes:
9053   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
9054   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
9055   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
9056   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
9057 
9058   The partitioned mesh is
9059   ```
9060   (2)--0--(3)--1--(4)    (1)--0--(2)
9061   ```
9062   and its global numbering is
9063   ```
9064   (3)--0--(4)--1--(5)--2--(6)
9065   ```
9066   Then the global numbering is provided as
9067   ```
9068   [0] Number of indices in set 5
9069   [0] 0 0
9070   [0] 1 1
9071   [0] 2 3
9072   [0] 3 4
9073   [0] 4 -6
9074   [1] Number of indices in set 3
9075   [1] 0 2
9076   [1] 1 5
9077   [1] 2 6
9078   ```
9079 
9080 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9081 @*/
9082 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9083 {
9084   IS        nums[4];
9085   PetscInt  depths[4], gdepths[4], starts[4];
9086   PetscInt  depth, d, shift = 0;
9087   PetscBool empty = PETSC_FALSE;
9088 
9089   PetscFunctionBegin;
9090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9091   PetscCall(DMPlexGetDepth(dm, &depth));
9092   // For unstratified meshes use dim instead of depth
9093   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9094   // If any stratum is empty, we must mark all empty
9095   for (d = 0; d <= depth; ++d) {
9096     PetscInt end;
9097 
9098     depths[d] = depth - d;
9099     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9100     if (!(starts[d] - end)) empty = PETSC_TRUE;
9101   }
9102   if (empty)
9103     for (d = 0; d <= depth; ++d) {
9104       depths[d] = -1;
9105       starts[d] = -1;
9106     }
9107   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9108   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9109   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]);
9110   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9111   for (d = 0; d <= depth; ++d) {
9112     PetscInt pStart, pEnd, gsize;
9113 
9114     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9115     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9116     shift += gsize;
9117   }
9118   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9119   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9120   PetscFunctionReturn(PETSC_SUCCESS);
9121 }
9122 
9123 /*@
9124   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9125 
9126   Collective
9127 
9128   Input Parameter:
9129 . dm - The `DMPLEX` object
9130 
9131   Output Parameter:
9132 . globalEdgeNumbers - Global numbers for all edges on this process
9133 
9134   Level: developer
9135 
9136   Notes:
9137   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).
9138 
9139 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9140 @*/
9141 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9142 {
9143   PetscSF  sf;
9144   PetscInt eStart, eEnd;
9145 
9146   PetscFunctionBegin;
9147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9148   PetscCall(DMGetPointSF(dm, &sf));
9149   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9150   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9151   PetscFunctionReturn(PETSC_SUCCESS);
9152 }
9153 
9154 /*@
9155   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9156 
9157   Input Parameter:
9158 . dm - The `DMPLEX` object
9159 
9160   Output Parameter:
9161 . ranks - The rank field
9162 
9163   Options Database Key:
9164 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9165 
9166   Level: intermediate
9167 
9168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9169 @*/
9170 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9171 {
9172   DM             rdm;
9173   PetscFE        fe;
9174   PetscScalar   *r;
9175   PetscMPIInt    rank;
9176   DMPolytopeType ct;
9177   PetscInt       dim, cStart, cEnd, c;
9178   PetscBool      simplex;
9179 
9180   PetscFunctionBeginUser;
9181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9182   PetscAssertPointer(ranks, 2);
9183   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9184   PetscCall(DMClone(dm, &rdm));
9185   PetscCall(DMGetDimension(rdm, &dim));
9186   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9187   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9188   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9189   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9190   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9191   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9192   PetscCall(PetscFEDestroy(&fe));
9193   PetscCall(DMCreateDS(rdm));
9194   PetscCall(DMCreateGlobalVector(rdm, ranks));
9195   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9196   PetscCall(VecGetArray(*ranks, &r));
9197   for (c = cStart; c < cEnd; ++c) {
9198     PetscScalar *lr;
9199 
9200     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9201     if (lr) *lr = rank;
9202   }
9203   PetscCall(VecRestoreArray(*ranks, &r));
9204   PetscCall(DMDestroy(&rdm));
9205   PetscFunctionReturn(PETSC_SUCCESS);
9206 }
9207 
9208 /*@
9209   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9210 
9211   Input Parameters:
9212 + dm    - The `DMPLEX`
9213 - label - The `DMLabel`
9214 
9215   Output Parameter:
9216 . val - The label value field
9217 
9218   Options Database Key:
9219 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9220 
9221   Level: intermediate
9222 
9223 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9224 @*/
9225 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9226 {
9227   DM             rdm, plex;
9228   Vec            lval;
9229   PetscSection   section;
9230   PetscFE        fe;
9231   PetscScalar   *v;
9232   PetscInt       dim, pStart, pEnd, p, cStart;
9233   DMPolytopeType ct;
9234   char           name[PETSC_MAX_PATH_LEN];
9235   const char    *lname, *prefix;
9236 
9237   PetscFunctionBeginUser;
9238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9239   PetscAssertPointer(label, 2);
9240   PetscAssertPointer(val, 3);
9241   PetscCall(DMClone(dm, &rdm));
9242   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9243   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9244   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9245   PetscCall(DMDestroy(&plex));
9246   PetscCall(DMGetDimension(rdm, &dim));
9247   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9248   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9249   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9250   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9251   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9252   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9253   PetscCall(PetscFEDestroy(&fe));
9254   PetscCall(DMCreateDS(rdm));
9255   PetscCall(DMCreateGlobalVector(rdm, val));
9256   PetscCall(DMCreateLocalVector(rdm, &lval));
9257   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9258   PetscCall(DMGetLocalSection(rdm, &section));
9259   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9260   PetscCall(VecGetArray(lval, &v));
9261   for (p = pStart; p < pEnd; ++p) {
9262     PetscInt cval, dof, off;
9263 
9264     PetscCall(PetscSectionGetDof(section, p, &dof));
9265     if (!dof) continue;
9266     PetscCall(DMLabelGetValue(label, p, &cval));
9267     PetscCall(PetscSectionGetOffset(section, p, &off));
9268     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9269   }
9270   PetscCall(VecRestoreArray(lval, &v));
9271   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9272   PetscCall(VecDestroy(&lval));
9273   PetscCall(DMDestroy(&rdm));
9274   PetscFunctionReturn(PETSC_SUCCESS);
9275 }
9276 
9277 /*@
9278   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9279 
9280   Input Parameter:
9281 . dm - The `DMPLEX` object
9282 
9283   Level: developer
9284 
9285   Notes:
9286   This is a useful diagnostic when creating meshes programmatically.
9287 
9288   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9289 
9290 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9291 @*/
9292 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9293 {
9294   PetscSection    coneSection, supportSection;
9295   const PetscInt *cone, *support;
9296   PetscInt        coneSize, c, supportSize, s;
9297   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9298   PetscBool       storagecheck = PETSC_TRUE;
9299 
9300   PetscFunctionBegin;
9301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9302   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9303   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9304   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9305   /* Check that point p is found in the support of its cone points, and vice versa */
9306   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9307   for (p = pStart; p < pEnd; ++p) {
9308     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9309     PetscCall(DMPlexGetCone(dm, p, &cone));
9310     for (c = 0; c < coneSize; ++c) {
9311       PetscBool dup = PETSC_FALSE;
9312       PetscInt  d;
9313       for (d = c - 1; d >= 0; --d) {
9314         if (cone[c] == cone[d]) {
9315           dup = PETSC_TRUE;
9316           break;
9317         }
9318       }
9319       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9320       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9321       for (s = 0; s < supportSize; ++s) {
9322         if (support[s] == p) break;
9323       }
9324       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9325         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9326         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9327         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9328         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9329         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9330         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9331         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]);
9332         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9333       }
9334     }
9335     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9336     if (p != pp) {
9337       storagecheck = PETSC_FALSE;
9338       continue;
9339     }
9340     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9341     PetscCall(DMPlexGetSupport(dm, p, &support));
9342     for (s = 0; s < supportSize; ++s) {
9343       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9344       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9345       for (c = 0; c < coneSize; ++c) {
9346         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9347         if (cone[c] != pp) {
9348           c = 0;
9349           break;
9350         }
9351         if (cone[c] == p) break;
9352       }
9353       if (c >= coneSize) {
9354         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9355         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9356         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9357         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9358         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9359         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9360         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9361       }
9362     }
9363   }
9364   if (storagecheck) {
9365     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9366     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9367     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9368   }
9369   PetscFunctionReturn(PETSC_SUCCESS);
9370 }
9371 
9372 /*
9373   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.
9374 */
9375 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9376 {
9377   DMPolytopeType  cct;
9378   PetscInt        ptpoints[4];
9379   const PetscInt *cone, *ccone, *ptcone;
9380   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9381 
9382   PetscFunctionBegin;
9383   *unsplit = 0;
9384   switch (ct) {
9385   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9386     ptpoints[npt++] = c;
9387     break;
9388   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9389     PetscCall(DMPlexGetCone(dm, c, &cone));
9390     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9391     for (cp = 0; cp < coneSize; ++cp) {
9392       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9393       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9394     }
9395     break;
9396   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9397   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9398     PetscCall(DMPlexGetCone(dm, c, &cone));
9399     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9400     for (cp = 0; cp < coneSize; ++cp) {
9401       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9402       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9403       for (ccp = 0; ccp < cconeSize; ++ccp) {
9404         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9405         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9406           PetscInt p;
9407           for (p = 0; p < npt; ++p)
9408             if (ptpoints[p] == ccone[ccp]) break;
9409           if (p == npt) ptpoints[npt++] = ccone[ccp];
9410         }
9411       }
9412     }
9413     break;
9414   default:
9415     break;
9416   }
9417   for (pt = 0; pt < npt; ++pt) {
9418     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9419     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9420   }
9421   PetscFunctionReturn(PETSC_SUCCESS);
9422 }
9423 
9424 /*@
9425   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9426 
9427   Input Parameters:
9428 + dm         - The `DMPLEX` object
9429 - cellHeight - Normally 0
9430 
9431   Level: developer
9432 
9433   Notes:
9434   This is a useful diagnostic when creating meshes programmatically.
9435   Currently applicable only to homogeneous simplex or tensor meshes.
9436 
9437   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9438 
9439 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9440 @*/
9441 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9442 {
9443   DMPlexInterpolatedFlag interp;
9444   DMPolytopeType         ct;
9445   PetscInt               vStart, vEnd, cStart, cEnd, c;
9446 
9447   PetscFunctionBegin;
9448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9449   PetscCall(DMPlexIsInterpolated(dm, &interp));
9450   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9451   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9452   for (c = cStart; c < cEnd; ++c) {
9453     PetscInt *closure = NULL;
9454     PetscInt  coneSize, closureSize, cl, Nv = 0;
9455 
9456     PetscCall(DMPlexGetCellType(dm, c, &ct));
9457     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9458     if (interp == DMPLEX_INTERPOLATED_FULL) {
9459       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9460       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));
9461     }
9462     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9463     for (cl = 0; cl < closureSize * 2; cl += 2) {
9464       const PetscInt p = closure[cl];
9465       if ((p >= vStart) && (p < vEnd)) ++Nv;
9466     }
9467     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9468     /* Special Case: Tensor faces with identified vertices */
9469     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9470       PetscInt unsplit;
9471 
9472       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9473       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9474     }
9475     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));
9476   }
9477   PetscFunctionReturn(PETSC_SUCCESS);
9478 }
9479 
9480 /*@
9481   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9482 
9483   Collective
9484 
9485   Input Parameters:
9486 + dm         - The `DMPLEX` object
9487 - cellHeight - Normally 0
9488 
9489   Level: developer
9490 
9491   Notes:
9492   This is a useful diagnostic when creating meshes programmatically.
9493   This routine is only relevant for meshes that are fully interpolated across all ranks.
9494   It will error out if a partially interpolated mesh is given on some rank.
9495   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9496 
9497   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9498 
9499 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9500 @*/
9501 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9502 {
9503   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9504   DMPlexInterpolatedFlag interpEnum;
9505 
9506   PetscFunctionBegin;
9507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9508   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9509   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9510   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9511     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9512     PetscFunctionReturn(PETSC_SUCCESS);
9513   }
9514 
9515   PetscCall(DMGetDimension(dm, &dim));
9516   PetscCall(DMPlexGetDepth(dm, &depth));
9517   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9518   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9519     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9520     for (c = cStart; c < cEnd; ++c) {
9521       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9522       const DMPolytopeType *faceTypes;
9523       DMPolytopeType        ct;
9524       PetscInt              numFaces, coneSize, f;
9525       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9526 
9527       PetscCall(DMPlexGetCellType(dm, c, &ct));
9528       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9529       if (unsplit) continue;
9530       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9531       PetscCall(DMPlexGetCone(dm, c, &cone));
9532       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9533       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9534       for (cl = 0; cl < closureSize * 2; cl += 2) {
9535         const PetscInt p = closure[cl];
9536         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9537       }
9538       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9539       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);
9540       for (f = 0; f < numFaces; ++f) {
9541         DMPolytopeType fct;
9542         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9543 
9544         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9545         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9546         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9547           const PetscInt p = fclosure[cl];
9548           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9549         }
9550         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]);
9551         for (v = 0; v < fnumCorners; ++v) {
9552           if (fclosure[v] != faces[fOff + v]) {
9553             PetscInt v1;
9554 
9555             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9556             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9557             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9558             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9559             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9560             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]);
9561           }
9562         }
9563         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9564         fOff += faceSizes[f];
9565       }
9566       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9567       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9568     }
9569   }
9570   PetscFunctionReturn(PETSC_SUCCESS);
9571 }
9572 
9573 /*@
9574   DMPlexCheckGeometry - Check the geometry of mesh cells
9575 
9576   Input Parameter:
9577 . dm - The `DMPLEX` object
9578 
9579   Level: developer
9580 
9581   Notes:
9582   This is a useful diagnostic when creating meshes programmatically.
9583 
9584   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9585 
9586 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9587 @*/
9588 PetscErrorCode DMPlexCheckGeometry(DM dm)
9589 {
9590   Vec       coordinates;
9591   PetscReal detJ, J[9], refVol = 1.0;
9592   PetscReal vol;
9593   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9594 
9595   PetscFunctionBegin;
9596   PetscCall(DMGetDimension(dm, &dim));
9597   PetscCall(DMGetCoordinateDim(dm, &dE));
9598   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9599   PetscCall(DMPlexGetDepth(dm, &depth));
9600   for (d = 0; d < dim; ++d) refVol *= 2.0;
9601   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9602   /* Make sure local coordinates are created, because that step is collective */
9603   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9604   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9605   for (c = cStart; c < cEnd; ++c) {
9606     DMPolytopeType ct;
9607     PetscInt       unsplit;
9608     PetscBool      ignoreZeroVol = PETSC_FALSE;
9609 
9610     PetscCall(DMPlexGetCellType(dm, c, &ct));
9611     switch (ct) {
9612     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9613     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9614     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9615       ignoreZeroVol = PETSC_TRUE;
9616       break;
9617     default:
9618       break;
9619     }
9620     switch (ct) {
9621     case DM_POLYTOPE_TRI_PRISM:
9622     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9623     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9624     case DM_POLYTOPE_PYRAMID:
9625       continue;
9626     default:
9627       break;
9628     }
9629     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9630     if (unsplit) continue;
9631     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9632     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);
9633     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9634     /* This should work with periodicity since DG coordinates should be used */
9635     if (depth > 1) {
9636       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9637       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);
9638       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9639     }
9640   }
9641   PetscFunctionReturn(PETSC_SUCCESS);
9642 }
9643 
9644 /*@
9645   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9646 
9647   Collective
9648 
9649   Input Parameters:
9650 + dm              - The `DMPLEX` object
9651 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9652 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9653 
9654   Level: developer
9655 
9656   Notes:
9657   This is mainly intended for debugging/testing purposes.
9658 
9659   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9660 
9661   Extra roots can come from periodic cuts, where additional points appear on the boundary
9662 
9663 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9664 @*/
9665 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9666 {
9667   PetscInt           l, nleaves, nroots, overlap;
9668   const PetscInt    *locals;
9669   const PetscSFNode *remotes;
9670   PetscBool          distributed;
9671   MPI_Comm           comm;
9672   PetscMPIInt        rank;
9673 
9674   PetscFunctionBegin;
9675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9676   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9677   else pointSF = dm->sf;
9678   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9679   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9680   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9681   {
9682     PetscMPIInt mpiFlag;
9683 
9684     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9685     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9686   }
9687   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9688   PetscCall(DMPlexIsDistributed(dm, &distributed));
9689   if (!distributed) {
9690     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);
9691     PetscFunctionReturn(PETSC_SUCCESS);
9692   }
9693   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);
9694   PetscCall(DMPlexGetOverlap(dm, &overlap));
9695 
9696   /* Check SF graph is compatible with DMPlex chart */
9697   {
9698     PetscInt pStart, pEnd, maxLeaf;
9699 
9700     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9701     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9702     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9703     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9704   }
9705 
9706   /* Check there are no cells in interface */
9707   if (!overlap) {
9708     PetscInt cellHeight, cStart, cEnd;
9709 
9710     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9711     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9712     for (l = 0; l < nleaves; ++l) {
9713       const PetscInt point = locals ? locals[l] : l;
9714 
9715       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9716     }
9717   }
9718 
9719   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9720   {
9721     const PetscInt *rootdegree;
9722 
9723     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9724     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9725     for (l = 0; l < nleaves; ++l) {
9726       const PetscInt  point = locals ? locals[l] : l;
9727       const PetscInt *cone;
9728       PetscInt        coneSize, c, idx;
9729 
9730       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9731       PetscCall(DMPlexGetCone(dm, point, &cone));
9732       for (c = 0; c < coneSize; ++c) {
9733         if (!rootdegree[cone[c]]) {
9734           if (locals) {
9735             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9736           } else {
9737             idx = (cone[c] < nleaves) ? cone[c] : -1;
9738           }
9739           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9740         }
9741       }
9742     }
9743   }
9744   PetscFunctionReturn(PETSC_SUCCESS);
9745 }
9746 
9747 /*@
9748   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9749 
9750   Collective
9751 
9752   Input Parameter:
9753 . dm - The `DMPLEX` object
9754 
9755   Level: developer
9756 
9757   Notes:
9758   This is mainly intended for debugging/testing purposes.
9759 
9760   Other cell types which are disconnected would be caught by the symmetry and face checks.
9761 
9762   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9763 
9764 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9765 @*/
9766 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9767 {
9768   PetscInt pStart, pEnd, vStart, vEnd;
9769 
9770   PetscFunctionBegin;
9771   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9772   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9773   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9774   for (PetscInt v = vStart; v < vEnd; ++v) {
9775     PetscInt suppSize;
9776 
9777     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9778     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9779   }
9780   PetscFunctionReturn(PETSC_SUCCESS);
9781 }
9782 
9783 /*@
9784   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9785 
9786   Input Parameter:
9787 . dm - The `DMPLEX` object
9788 
9789   Level: developer
9790 
9791   Notes:
9792   This is a useful diagnostic when creating meshes programmatically.
9793 
9794   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9795 
9796   Currently does not include `DMPlexCheckCellShape()`.
9797 
9798 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9799 @*/
9800 PetscErrorCode DMPlexCheck(DM dm)
9801 {
9802   PetscInt cellHeight;
9803 
9804   PetscFunctionBegin;
9805   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9806   PetscCall(DMPlexCheckSymmetry(dm));
9807   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9808   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9809   PetscCall(DMPlexCheckGeometry(dm));
9810   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9811   PetscCall(DMPlexCheckInterfaceCones(dm));
9812   PetscCall(DMPlexCheckOrphanVertices(dm));
9813   PetscFunctionReturn(PETSC_SUCCESS);
9814 }
9815 
9816 typedef struct cell_stats {
9817   PetscReal min, max, sum, squaresum;
9818   PetscInt  count;
9819 } cell_stats_t;
9820 
9821 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9822 {
9823   PetscInt i, N = *len;
9824 
9825   for (i = 0; i < N; i++) {
9826     cell_stats_t *A = (cell_stats_t *)a;
9827     cell_stats_t *B = (cell_stats_t *)b;
9828 
9829     B->min = PetscMin(A->min, B->min);
9830     B->max = PetscMax(A->max, B->max);
9831     B->sum += A->sum;
9832     B->squaresum += A->squaresum;
9833     B->count += A->count;
9834   }
9835 }
9836 
9837 /*@
9838   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9839 
9840   Collective
9841 
9842   Input Parameters:
9843 + dm        - The `DMPLEX` object
9844 . output    - If true, statistics will be displayed on `stdout`
9845 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9846 
9847   Level: developer
9848 
9849   Notes:
9850   This is mainly intended for debugging/testing purposes.
9851 
9852   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9853 
9854 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9855 @*/
9856 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9857 {
9858   DM           dmCoarse;
9859   cell_stats_t stats, globalStats;
9860   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9861   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9862   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9863   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9864   PetscMPIInt  rank, size;
9865 
9866   PetscFunctionBegin;
9867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9868   stats.min = PETSC_MAX_REAL;
9869   stats.max = PETSC_MIN_REAL;
9870   stats.sum = stats.squaresum = 0.;
9871   stats.count                 = 0;
9872 
9873   PetscCallMPI(MPI_Comm_size(comm, &size));
9874   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9875   PetscCall(DMGetCoordinateDim(dm, &cdim));
9876   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9877   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9878   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9879   for (c = cStart; c < cEnd; c++) {
9880     PetscInt  i;
9881     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9882 
9883     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9884     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9885     for (i = 0; i < PetscSqr(cdim); ++i) {
9886       frobJ += J[i] * J[i];
9887       frobInvJ += invJ[i] * invJ[i];
9888     }
9889     cond2 = frobJ * frobInvJ;
9890     cond  = PetscSqrtReal(cond2);
9891 
9892     stats.min = PetscMin(stats.min, cond);
9893     stats.max = PetscMax(stats.max, cond);
9894     stats.sum += cond;
9895     stats.squaresum += cond2;
9896     stats.count++;
9897     if (output && cond > limit) {
9898       PetscSection coordSection;
9899       Vec          coordsLocal;
9900       PetscScalar *coords = NULL;
9901       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9902 
9903       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9904       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9905       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9906       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9907       for (i = 0; i < Nv / cdim; ++i) {
9908         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9909         for (d = 0; d < cdim; ++d) {
9910           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9911           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9912         }
9913         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9914       }
9915       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9916       for (cl = 0; cl < clSize * 2; cl += 2) {
9917         const PetscInt edge = closure[cl];
9918 
9919         if ((edge >= eStart) && (edge < eEnd)) {
9920           PetscReal len;
9921 
9922           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9923           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9924         }
9925       }
9926       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9927       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9928     }
9929   }
9930   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9931 
9932   if (size > 1) {
9933     PetscMPIInt  blockLengths[2] = {4, 1};
9934     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9935     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9936     MPI_Op       statReduce;
9937 
9938     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9939     PetscCallMPI(MPI_Type_commit(&statType));
9940     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9941     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9942     PetscCallMPI(MPI_Op_free(&statReduce));
9943     PetscCallMPI(MPI_Type_free(&statType));
9944   } else {
9945     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9946   }
9947   if (rank == 0) {
9948     count = globalStats.count;
9949     min   = globalStats.min;
9950     max   = globalStats.max;
9951     mean  = globalStats.sum / globalStats.count;
9952     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9953   }
9954 
9955   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));
9956   PetscCall(PetscFree2(J, invJ));
9957 
9958   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9959   if (dmCoarse) {
9960     PetscBool isplex;
9961 
9962     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9963     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9964   }
9965   PetscFunctionReturn(PETSC_SUCCESS);
9966 }
9967 
9968 /*@
9969   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9970   orthogonal quality below given tolerance.
9971 
9972   Collective
9973 
9974   Input Parameters:
9975 + dm   - The `DMPLEX` object
9976 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9977 - atol - [0, 1] Absolute tolerance for tagging cells.
9978 
9979   Output Parameters:
9980 + OrthQual      - `Vec` containing orthogonal quality per cell
9981 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9982 
9983   Options Database Keys:
9984 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9985 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9986 
9987   Level: intermediate
9988 
9989   Notes:
9990   Orthogonal quality is given by the following formula\:
9991 
9992   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9993 
9994   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
9995   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9996   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9997   calculating the cosine of the angle between these vectors.
9998 
9999   Orthogonal quality ranges from 1 (best) to 0 (worst).
10000 
10001   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
10002   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
10003 
10004   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
10005 
10006 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
10007 @*/
10008 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
10009 {
10010   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
10011   PetscInt              *idx;
10012   PetscScalar           *oqVals;
10013   const PetscScalar     *cellGeomArr, *faceGeomArr;
10014   PetscReal             *ci, *fi, *Ai;
10015   MPI_Comm               comm;
10016   Vec                    cellgeom, facegeom;
10017   DM                     dmFace, dmCell;
10018   IS                     glob;
10019   ISLocalToGlobalMapping ltog;
10020   PetscViewer            vwr;
10021 
10022   PetscFunctionBegin;
10023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10024   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
10025   PetscAssertPointer(OrthQual, 4);
10026   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
10027   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
10028   PetscCall(DMGetDimension(dm, &nc));
10029   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
10030   {
10031     DMPlexInterpolatedFlag interpFlag;
10032 
10033     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
10034     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
10035       PetscMPIInt rank;
10036 
10037       PetscCallMPI(MPI_Comm_rank(comm, &rank));
10038       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
10039     }
10040   }
10041   if (OrthQualLabel) {
10042     PetscAssertPointer(OrthQualLabel, 5);
10043     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
10044     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
10045   } else {
10046     *OrthQualLabel = NULL;
10047   }
10048   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
10049   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
10050   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
10051   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
10052   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
10053   PetscCall(VecCreate(comm, OrthQual));
10054   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
10055   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
10056   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
10057   PetscCall(VecSetUp(*OrthQual));
10058   PetscCall(ISDestroy(&glob));
10059   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
10060   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
10061   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
10062   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10063   PetscCall(VecGetDM(cellgeom, &dmCell));
10064   PetscCall(VecGetDM(facegeom, &dmFace));
10065   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10066   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10067     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10068     PetscInt         cellarr[2], *adj = NULL;
10069     PetscScalar     *cArr, *fArr;
10070     PetscReal        minvalc = 1.0, minvalf = 1.0;
10071     PetscFVCellGeom *cg;
10072 
10073     idx[cellIter] = cell - cStart;
10074     cellarr[0]    = cell;
10075     /* Make indexing into cellGeom easier */
10076     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10077     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10078     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10079     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10080     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10081       PetscInt         i;
10082       const PetscInt   neigh  = adj[cellneigh];
10083       PetscReal        normci = 0, normfi = 0, normai = 0;
10084       PetscFVCellGeom *cgneigh;
10085       PetscFVFaceGeom *fg;
10086 
10087       /* Don't count ourselves in the neighbor list */
10088       if (neigh == cell) continue;
10089       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10090       cellarr[1] = neigh;
10091       {
10092         PetscInt        numcovpts;
10093         const PetscInt *covpts;
10094 
10095         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10096         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10097         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10098       }
10099 
10100       /* Compute c_i, f_i and their norms */
10101       for (i = 0; i < nc; i++) {
10102         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10103         fi[i] = fg->centroid[i] - cg->centroid[i];
10104         Ai[i] = fg->normal[i];
10105         normci += PetscPowReal(ci[i], 2);
10106         normfi += PetscPowReal(fi[i], 2);
10107         normai += PetscPowReal(Ai[i], 2);
10108       }
10109       normci = PetscSqrtReal(normci);
10110       normfi = PetscSqrtReal(normfi);
10111       normai = PetscSqrtReal(normai);
10112 
10113       /* Normalize and compute for each face-cell-normal pair */
10114       for (i = 0; i < nc; i++) {
10115         ci[i] = ci[i] / normci;
10116         fi[i] = fi[i] / normfi;
10117         Ai[i] = Ai[i] / normai;
10118         /* PetscAbs because I don't know if normals are guaranteed to point out */
10119         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10120         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10121       }
10122       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10123       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10124     }
10125     PetscCall(PetscFree(adj));
10126     PetscCall(PetscFree2(cArr, fArr));
10127     /* Defer to cell if they're equal */
10128     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10129     if (OrthQualLabel) {
10130       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10131     }
10132   }
10133   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10134   PetscCall(VecAssemblyBegin(*OrthQual));
10135   PetscCall(VecAssemblyEnd(*OrthQual));
10136   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10137   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10138   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10139   if (OrthQualLabel) {
10140     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10141   }
10142   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10143   PetscCall(PetscViewerDestroy(&vwr));
10144   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10145   PetscFunctionReturn(PETSC_SUCCESS);
10146 }
10147 
10148 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10149  * interpolator construction */
10150 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10151 {
10152   PetscSection section, newSection, gsection;
10153   PetscSF      sf;
10154   PetscBool    hasConstraints, ghasConstraints;
10155 
10156   PetscFunctionBegin;
10157   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10158   PetscAssertPointer(odm, 2);
10159   PetscCall(DMGetLocalSection(dm, &section));
10160   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10161   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10162   if (!ghasConstraints) {
10163     PetscCall(PetscObjectReference((PetscObject)dm));
10164     *odm = dm;
10165     PetscFunctionReturn(PETSC_SUCCESS);
10166   }
10167   PetscCall(DMClone(dm, odm));
10168   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10169   PetscCall(DMGetLocalSection(*odm, &newSection));
10170   PetscCall(DMGetPointSF(*odm, &sf));
10171   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10172   PetscCall(DMSetGlobalSection(*odm, gsection));
10173   PetscCall(PetscSectionDestroy(&gsection));
10174   PetscFunctionReturn(PETSC_SUCCESS);
10175 }
10176 
10177 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10178 {
10179   DM        dmco, dmfo;
10180   Mat       interpo;
10181   Vec       rscale;
10182   Vec       cglobalo, clocal;
10183   Vec       fglobal, fglobalo, flocal;
10184   PetscBool regular;
10185 
10186   PetscFunctionBegin;
10187   PetscCall(DMGetFullDM(dmc, &dmco));
10188   PetscCall(DMGetFullDM(dmf, &dmfo));
10189   PetscCall(DMSetCoarseDM(dmfo, dmco));
10190   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10191   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10192   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10193   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10194   PetscCall(DMCreateLocalVector(dmc, &clocal));
10195   PetscCall(VecSet(cglobalo, 0.));
10196   PetscCall(VecSet(clocal, 0.));
10197   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10198   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10199   PetscCall(DMCreateLocalVector(dmf, &flocal));
10200   PetscCall(VecSet(fglobal, 0.));
10201   PetscCall(VecSet(fglobalo, 0.));
10202   PetscCall(VecSet(flocal, 0.));
10203   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10204   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10205   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10206   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10207   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10208   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10209   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10210   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10211   *shift = fglobal;
10212   PetscCall(VecDestroy(&flocal));
10213   PetscCall(VecDestroy(&fglobalo));
10214   PetscCall(VecDestroy(&clocal));
10215   PetscCall(VecDestroy(&cglobalo));
10216   PetscCall(VecDestroy(&rscale));
10217   PetscCall(MatDestroy(&interpo));
10218   PetscCall(DMDestroy(&dmfo));
10219   PetscCall(DMDestroy(&dmco));
10220   PetscFunctionReturn(PETSC_SUCCESS);
10221 }
10222 
10223 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10224 {
10225   PetscObject shifto;
10226   Vec         shift;
10227 
10228   PetscFunctionBegin;
10229   if (!interp) {
10230     Vec rscale;
10231 
10232     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10233     PetscCall(VecDestroy(&rscale));
10234   } else {
10235     PetscCall(PetscObjectReference((PetscObject)interp));
10236   }
10237   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10238   if (!shifto) {
10239     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10240     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10241     shifto = (PetscObject)shift;
10242     PetscCall(VecDestroy(&shift));
10243   }
10244   shift = (Vec)shifto;
10245   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10246   PetscCall(VecAXPY(fineSol, 1.0, shift));
10247   PetscCall(MatDestroy(&interp));
10248   PetscFunctionReturn(PETSC_SUCCESS);
10249 }
10250 
10251 /* Pointwise interpolation
10252      Just code FEM for now
10253      u^f = I u^c
10254      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10255      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10256      I_{ij} = psi^f_i phi^c_j
10257 */
10258 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10259 {
10260   PetscSection gsc, gsf;
10261   PetscInt     m, n;
10262   void        *ctx;
10263   DM           cdm;
10264   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10265 
10266   PetscFunctionBegin;
10267   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10268   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10269   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10270   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10271 
10272   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10273   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10274   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10275   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10276   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10277 
10278   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10279   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10280   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10281   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10282   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10283   if (scaling) {
10284     /* Use naive scaling */
10285     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10286   }
10287   PetscFunctionReturn(PETSC_SUCCESS);
10288 }
10289 
10290 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10291 {
10292   VecScatter ctx;
10293 
10294   PetscFunctionBegin;
10295   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10296   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10297   PetscCall(VecScatterDestroy(&ctx));
10298   PetscFunctionReturn(PETSC_SUCCESS);
10299 }
10300 
10301 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[])
10302 {
10303   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10304   const PetscInt Nc = uOff[f + 1] - uOff[f];
10305   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10306 }
10307 
10308 // The assumption here is that the test field is a vector and the basis field is a scalar (so we need the gradient)
10309 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[])
10310 {
10311   for (PetscInt c = 0; c < dim; ++c) g1[c * dim + c] = 1.0;
10312 }
10313 
10314 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10315 {
10316   DM           dmc;
10317   PetscDS      ds;
10318   Vec          ones, locmass;
10319   IS           cellIS;
10320   PetscFormKey key;
10321   PetscInt     depth;
10322 
10323   PetscFunctionBegin;
10324   PetscCall(DMClone(dm, &dmc));
10325   PetscCall(DMCopyDisc(dm, dmc));
10326   PetscCall(DMGetDS(dmc, &ds));
10327   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10328   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10329   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10330   else PetscCall(DMGetLocalVector(dm, &locmass));
10331   PetscCall(DMGetLocalVector(dm, &ones));
10332   PetscCall(DMPlexGetDepth(dm, &depth));
10333   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10334   PetscCall(VecSet(locmass, 0.0));
10335   PetscCall(VecSet(ones, 1.0));
10336   key.label = NULL;
10337   key.value = 0;
10338   key.field = 0;
10339   key.part  = 0;
10340   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10341   PetscCall(ISDestroy(&cellIS));
10342   if (mass) {
10343     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10344     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10345   }
10346   PetscCall(DMRestoreLocalVector(dm, &ones));
10347   if (lmass) *lmass = locmass;
10348   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10349   PetscCall(DMDestroy(&dmc));
10350   PetscFunctionReturn(PETSC_SUCCESS);
10351 }
10352 
10353 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10354 {
10355   PetscSection gsc, gsf;
10356   PetscInt     m, n;
10357   void        *ctx;
10358   DM           cdm;
10359   PetscBool    regular;
10360 
10361   PetscFunctionBegin;
10362   if (dmFine == dmCoarse) {
10363     DM            dmc;
10364     PetscDS       ds;
10365     PetscWeakForm wf;
10366     Vec           u;
10367     IS            cellIS;
10368     PetscFormKey  key;
10369     PetscInt      depth;
10370 
10371     PetscCall(DMClone(dmFine, &dmc));
10372     PetscCall(DMCopyDisc(dmFine, dmc));
10373     PetscCall(DMGetDS(dmc, &ds));
10374     PetscCall(PetscDSGetWeakForm(ds, &wf));
10375     PetscCall(PetscWeakFormClear(wf));
10376     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10377     PetscCall(DMCreateMatrix(dmc, mass));
10378     PetscCall(DMGetLocalVector(dmc, &u));
10379     PetscCall(DMPlexGetDepth(dmc, &depth));
10380     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10381     PetscCall(MatZeroEntries(*mass));
10382     key.label = NULL;
10383     key.value = 0;
10384     key.field = 0;
10385     key.part  = 0;
10386     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10387     PetscCall(ISDestroy(&cellIS));
10388     PetscCall(DMRestoreLocalVector(dmc, &u));
10389     PetscCall(DMDestroy(&dmc));
10390   } else {
10391     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10392     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10393     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10394     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10395 
10396     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10397     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10398     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10399     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10400 
10401     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10402     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10403     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10404     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10405   }
10406   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10407   PetscFunctionReturn(PETSC_SUCCESS);
10408 }
10409 
10410 PetscErrorCode DMCreateGradientMatrix_Plex(DM dmc, DM dmr, Mat *derv)
10411 {
10412   PetscSection gsc, gsf;
10413   PetscInt     m, n;
10414   void        *ctx;
10415 
10416   PetscFunctionBegin;
10417   PetscCall(DMGetGlobalSection(dmr, &gsf));
10418   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10419   PetscCall(DMGetGlobalSection(dmc, &gsc));
10420   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10421 
10422   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmc), derv));
10423   PetscCall(PetscObjectSetName((PetscObject)*derv, "Plex Derivative Matrix"));
10424   PetscCall(MatSetSizes(*derv, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10425   PetscCall(MatSetType(*derv, dmc->mattype));
10426 
10427   PetscCall(DMGetApplicationContext(dmr, &ctx));
10428   {
10429     DM            ndmr;
10430     PetscDS       ds;
10431     PetscWeakForm wf;
10432     Vec           u;
10433     IS            cellIS;
10434     PetscFormKey  key;
10435     PetscInt      depth, Nf;
10436 
10437     PetscCall(DMClone(dmr, &ndmr));
10438     PetscCall(DMCopyDisc(dmr, ndmr));
10439     PetscCall(DMGetDS(ndmr, &ds));
10440     PetscCall(PetscDSGetWeakForm(ds, &wf));
10441     PetscCall(PetscWeakFormClear(wf));
10442     PetscCall(PetscDSGetNumFields(ds, &Nf));
10443     for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, NULL, g1_identity_private, NULL, NULL));
10444     PetscCall(DMGetLocalVector(ndmr, &u));
10445     PetscCall(DMPlexGetDepth(ndmr, &depth));
10446     PetscCall(DMGetStratumIS(ndmr, "depth", depth, &cellIS));
10447     PetscCall(MatZeroEntries(*derv));
10448     key.label = NULL;
10449     key.value = 0;
10450     key.field = 0;
10451     key.part  = 0;
10452     PetscCall(DMPlexComputeJacobianByKeyGeneral(ndmr, dmc, key, cellIS, 0.0, 0.0, u, NULL, *derv, *derv, NULL));
10453     PetscCall(ISDestroy(&cellIS));
10454     PetscCall(DMRestoreLocalVector(ndmr, &u));
10455     PetscCall(DMDestroy(&ndmr));
10456   }
10457   PetscCall(MatViewFromOptions(*derv, NULL, "-gradient_mat_view"));
10458   PetscFunctionReturn(PETSC_SUCCESS);
10459 }
10460 
10461 /*@
10462   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10463 
10464   Input Parameter:
10465 . dm - The `DMPLEX` object
10466 
10467   Output Parameter:
10468 . regular - The flag
10469 
10470   Level: intermediate
10471 
10472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10473 @*/
10474 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10475 {
10476   PetscFunctionBegin;
10477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10478   PetscAssertPointer(regular, 2);
10479   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10480   PetscFunctionReturn(PETSC_SUCCESS);
10481 }
10482 
10483 /*@
10484   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10485 
10486   Input Parameters:
10487 + dm      - The `DMPLEX` object
10488 - regular - The flag
10489 
10490   Level: intermediate
10491 
10492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10493 @*/
10494 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10495 {
10496   PetscFunctionBegin;
10497   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10498   ((DM_Plex *)dm->data)->regularRefinement = regular;
10499   PetscFunctionReturn(PETSC_SUCCESS);
10500 }
10501 
10502 /*@
10503   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10504   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10505 
10506   Not Collective
10507 
10508   Input Parameter:
10509 . dm - The `DMPLEX` object
10510 
10511   Output Parameters:
10512 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10513 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10514 
10515   Level: intermediate
10516 
10517 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10518 @*/
10519 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10520 {
10521   DM_Plex *plex = (DM_Plex *)dm->data;
10522 
10523   PetscFunctionBegin;
10524   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10525   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10526   if (anchorSection) *anchorSection = plex->anchorSection;
10527   if (anchorIS) *anchorIS = plex->anchorIS;
10528   PetscFunctionReturn(PETSC_SUCCESS);
10529 }
10530 
10531 /*@
10532   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10533 
10534   Collective
10535 
10536   Input Parameters:
10537 + dm            - The `DMPLEX` object
10538 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10539                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10540 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10541 
10542   Level: intermediate
10543 
10544   Notes:
10545   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10546   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10547   combination of other points' degrees of freedom.
10548 
10549   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10550   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10551 
10552   The reference counts of `anchorSection` and `anchorIS` are incremented.
10553 
10554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10555 @*/
10556 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10557 {
10558   DM_Plex    *plex = (DM_Plex *)dm->data;
10559   PetscMPIInt result;
10560 
10561   PetscFunctionBegin;
10562   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10563   if (anchorSection) {
10564     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10565     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10566     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10567   }
10568   if (anchorIS) {
10569     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10570     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10571     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10572   }
10573 
10574   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10575   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10576   plex->anchorSection = anchorSection;
10577 
10578   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10579   PetscCall(ISDestroy(&plex->anchorIS));
10580   plex->anchorIS = anchorIS;
10581 
10582   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10583     PetscInt        size, a, pStart, pEnd;
10584     const PetscInt *anchors;
10585 
10586     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10587     PetscCall(ISGetLocalSize(anchorIS, &size));
10588     PetscCall(ISGetIndices(anchorIS, &anchors));
10589     for (a = 0; a < size; a++) {
10590       PetscInt p;
10591 
10592       p = anchors[a];
10593       if (p >= pStart && p < pEnd) {
10594         PetscInt dof;
10595 
10596         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10597         if (dof) {
10598           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10599           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10600         }
10601       }
10602     }
10603     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10604   }
10605   /* reset the generic constraints */
10606   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10607   PetscFunctionReturn(PETSC_SUCCESS);
10608 }
10609 
10610 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10611 {
10612   PetscSection anchorSection;
10613   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10614 
10615   PetscFunctionBegin;
10616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10617   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10618   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10619   PetscCall(PetscSectionGetNumFields(section, &numFields));
10620   if (numFields) {
10621     PetscInt f;
10622     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10623 
10624     for (f = 0; f < numFields; f++) {
10625       PetscInt numComp;
10626 
10627       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10628       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10629     }
10630   }
10631   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10632   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10633   pStart = PetscMax(pStart, sStart);
10634   pEnd   = PetscMin(pEnd, sEnd);
10635   pEnd   = PetscMax(pStart, pEnd);
10636   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10637   for (p = pStart; p < pEnd; p++) {
10638     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10639     if (dof) {
10640       PetscCall(PetscSectionGetDof(section, p, &dof));
10641       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10642       for (f = 0; f < numFields; f++) {
10643         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10644         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10645       }
10646     }
10647   }
10648   PetscCall(PetscSectionSetUp(*cSec));
10649   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10650   PetscFunctionReturn(PETSC_SUCCESS);
10651 }
10652 
10653 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10654 {
10655   PetscSection    aSec;
10656   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10657   const PetscInt *anchors;
10658   PetscInt        numFields, f;
10659   IS              aIS;
10660   MatType         mtype;
10661   PetscBool       iscuda, iskokkos;
10662 
10663   PetscFunctionBegin;
10664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10665   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10666   PetscCall(PetscSectionGetStorageSize(section, &n));
10667   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10668   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10669   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10670   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10671   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10672   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10673   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10674   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10675   else mtype = MATSEQAIJ;
10676   PetscCall(MatSetType(*cMat, mtype));
10677   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10678   PetscCall(ISGetIndices(aIS, &anchors));
10679   /* cSec will be a subset of aSec and section */
10680   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10681   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10682   PetscCall(PetscMalloc1(m + 1, &i));
10683   i[0] = 0;
10684   PetscCall(PetscSectionGetNumFields(section, &numFields));
10685   for (p = pStart; p < pEnd; p++) {
10686     PetscInt rDof, rOff, r;
10687 
10688     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10689     if (!rDof) continue;
10690     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10691     if (numFields) {
10692       for (f = 0; f < numFields; f++) {
10693         annz = 0;
10694         for (r = 0; r < rDof; r++) {
10695           a = anchors[rOff + r];
10696           if (a < sStart || a >= sEnd) continue;
10697           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10698           annz += aDof;
10699         }
10700         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10701         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10702         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10703       }
10704     } else {
10705       annz = 0;
10706       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10707       for (q = 0; q < dof; q++) {
10708         a = anchors[rOff + q];
10709         if (a < sStart || a >= sEnd) continue;
10710         PetscCall(PetscSectionGetDof(section, a, &aDof));
10711         annz += aDof;
10712       }
10713       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10714       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10715       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10716     }
10717   }
10718   nnz = i[m];
10719   PetscCall(PetscMalloc1(nnz, &j));
10720   offset = 0;
10721   for (p = pStart; p < pEnd; p++) {
10722     if (numFields) {
10723       for (f = 0; f < numFields; f++) {
10724         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10725         for (q = 0; q < dof; q++) {
10726           PetscInt rDof, rOff, r;
10727           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10728           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10729           for (r = 0; r < rDof; r++) {
10730             PetscInt s;
10731 
10732             a = anchors[rOff + r];
10733             if (a < sStart || a >= sEnd) continue;
10734             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10735             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10736             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10737           }
10738         }
10739       }
10740     } else {
10741       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10742       for (q = 0; q < dof; q++) {
10743         PetscInt rDof, rOff, r;
10744         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10745         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10746         for (r = 0; r < rDof; r++) {
10747           PetscInt s;
10748 
10749           a = anchors[rOff + r];
10750           if (a < sStart || a >= sEnd) continue;
10751           PetscCall(PetscSectionGetDof(section, a, &aDof));
10752           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10753           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10754         }
10755       }
10756     }
10757   }
10758   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10759   PetscCall(PetscFree(i));
10760   PetscCall(PetscFree(j));
10761   PetscCall(ISRestoreIndices(aIS, &anchors));
10762   PetscFunctionReturn(PETSC_SUCCESS);
10763 }
10764 
10765 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10766 {
10767   DM_Plex     *plex = (DM_Plex *)dm->data;
10768   PetscSection anchorSection, section, cSec;
10769   Mat          cMat;
10770 
10771   PetscFunctionBegin;
10772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10773   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10774   if (anchorSection) {
10775     PetscInt Nf;
10776 
10777     PetscCall(DMGetLocalSection(dm, &section));
10778     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10779     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10780     PetscCall(DMGetNumFields(dm, &Nf));
10781     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10782     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10783     PetscCall(PetscSectionDestroy(&cSec));
10784     PetscCall(MatDestroy(&cMat));
10785   }
10786   PetscFunctionReturn(PETSC_SUCCESS);
10787 }
10788 
10789 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10790 {
10791   IS           subis;
10792   PetscSection section, subsection;
10793 
10794   PetscFunctionBegin;
10795   PetscCall(DMGetLocalSection(dm, &section));
10796   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10797   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10798   /* Create subdomain */
10799   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10800   /* Create submodel */
10801   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10802   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10803   PetscCall(DMSetLocalSection(*subdm, subsection));
10804   PetscCall(PetscSectionDestroy(&subsection));
10805   PetscCall(DMCopyDisc(dm, *subdm));
10806   /* Create map from submodel to global model */
10807   if (is) {
10808     PetscSection    sectionGlobal, subsectionGlobal;
10809     IS              spIS;
10810     const PetscInt *spmap;
10811     PetscInt       *subIndices;
10812     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10813     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10814 
10815     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10816     PetscCall(ISGetIndices(spIS, &spmap));
10817     PetscCall(PetscSectionGetNumFields(section, &Nf));
10818     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10819     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10820     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10821     for (p = pStart; p < pEnd; ++p) {
10822       PetscInt gdof, pSubSize = 0;
10823 
10824       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10825       if (gdof > 0) {
10826         for (f = 0; f < Nf; ++f) {
10827           PetscInt fdof, fcdof;
10828 
10829           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10830           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10831           pSubSize += fdof - fcdof;
10832         }
10833         subSize += pSubSize;
10834         if (pSubSize) {
10835           if (bs < 0) {
10836             bs = pSubSize;
10837           } else if (bs != pSubSize) {
10838             /* Layout does not admit a pointwise block size */
10839             bs = 1;
10840           }
10841         }
10842       }
10843     }
10844     /* Must have same blocksize on all procs (some might have no points) */
10845     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10846     bsLocal[1] = bs;
10847     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10848     if (bsMinMax[0] != bsMinMax[1]) {
10849       bs = 1;
10850     } else {
10851       bs = bsMinMax[0];
10852     }
10853     PetscCall(PetscMalloc1(subSize, &subIndices));
10854     for (p = pStart; p < pEnd; ++p) {
10855       PetscInt gdof, goff;
10856 
10857       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10858       if (gdof > 0) {
10859         const PetscInt point = spmap[p];
10860 
10861         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10862         for (f = 0; f < Nf; ++f) {
10863           PetscInt fdof, fcdof, fc, f2, poff = 0;
10864 
10865           /* Can get rid of this loop by storing field information in the global section */
10866           for (f2 = 0; f2 < f; ++f2) {
10867             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10868             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10869             poff += fdof - fcdof;
10870           }
10871           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10872           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10873           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10874         }
10875       }
10876     }
10877     PetscCall(ISRestoreIndices(spIS, &spmap));
10878     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10879     if (bs > 1) {
10880       /* We need to check that the block size does not come from non-contiguous fields */
10881       PetscInt i, j, set = 1;
10882       for (i = 0; i < subSize; i += bs) {
10883         for (j = 0; j < bs; ++j) {
10884           if (subIndices[i + j] != subIndices[i] + j) {
10885             set = 0;
10886             break;
10887           }
10888         }
10889       }
10890       if (set) PetscCall(ISSetBlockSize(*is, bs));
10891     }
10892     // Attach nullspace
10893     if (dm->nullspaceConstructors) {
10894       for (f = 0; f < Nf; ++f) {
10895         (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10896         if ((*subdm)->nullspaceConstructors[f]) break;
10897       }
10898       if (f < Nf) {
10899         MatNullSpace nullSpace;
10900         PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10901 
10902         PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10903         PetscCall(MatNullSpaceDestroy(&nullSpace));
10904       }
10905     }
10906   }
10907   PetscFunctionReturn(PETSC_SUCCESS);
10908 }
10909 
10910 /*@
10911   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10912 
10913   Input Parameters:
10914 + dm    - The `DM`
10915 - dummy - unused argument
10916 
10917   Options Database Key:
10918 . -dm_plex_monitor_throughput - Activate the monitor
10919 
10920   Level: developer
10921 
10922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10923 @*/
10924 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10925 {
10926   PetscLogHandler default_handler;
10927 
10928   PetscFunctionBegin;
10929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10930   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10931   if (default_handler) {
10932     PetscLogEvent      event;
10933     PetscEventPerfInfo eventInfo;
10934     PetscLogDouble     cellRate, flopRate;
10935     PetscInt           cStart, cEnd, Nf, N;
10936     const char        *name;
10937 
10938     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10939     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10940     PetscCall(DMGetNumFields(dm, &Nf));
10941     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10942     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10943     N        = (cEnd - cStart) * Nf * eventInfo.count;
10944     flopRate = eventInfo.flops / eventInfo.time;
10945     cellRate = N / eventInfo.time;
10946     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));
10947   } else {
10948     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.");
10949   }
10950   PetscFunctionReturn(PETSC_SUCCESS);
10951 }
10952