xref: /petsc/src/dm/impls/plex/plex.c (revision 89a64d68d2f8dc8c4ae19e87b21a1e0b1231e51b)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_CreateBoxSFC, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 /* Logging support */
18 PetscLogEvent DMPLEX_DistributionView, DMPLEX_DistributionLoad;
19 
20 PetscBool  Plexcite       = PETSC_FALSE;
21 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
22                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
23                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
24                             "journal   = {SIAM Journal on Scientific Computing},\n"
25                             "volume    = {38},\n"
26                             "number    = {5},\n"
27                             "pages     = {S143--S155},\n"
28                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
29                             "doi       = {10.1137/15M1026092},\n"
30                             "year      = {2016},\n"
31                             "petsc_uses={DMPlex},\n}\n";
32 
33 PETSC_SINGLE_LIBRARY_INTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
34 
35 /*@
36   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
37 
38   Input Parameter:
39 . dm - The `DMPLEX` object
40 
41   Output Parameter:
42 . simplex - Flag checking for a simplex
43 
44   Level: intermediate
45 
46   Note:
47   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
48   If the mesh has no cells, this returns `PETSC_FALSE`.
49 
50 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
51 @*/
52 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
53 {
54   DMPolytopeType ct;
55   PetscInt       cStart, cEnd;
56 
57   PetscFunctionBegin;
58   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
59   if (cEnd <= cStart) {
60     *simplex = PETSC_FALSE;
61     PetscFunctionReturn(PETSC_SUCCESS);
62   }
63   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
64   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@
69   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
70 
71   Input Parameters:
72 + dm     - The `DMPLEX` object
73 - height - The cell height in the Plex, 0 is the default
74 
75   Output Parameters:
76 + cStart - The first "normal" cell, pass `NULL` if not needed
77 - cEnd   - The upper bound on "normal" cells, pass `NULL` if not needed
78 
79   Level: developer
80 
81   Note:
82   This function requires that tensor cells are ordered last.
83 
84 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
85 @*/
86 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PeOp PetscInt *cStart, PeOp PetscInt *cEnd)
87 {
88   DMLabel         ctLabel;
89   IS              valueIS;
90   const PetscInt *ctypes;
91   PetscBool       found = PETSC_FALSE;
92   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
93 
94   PetscFunctionBegin;
95   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
96   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
97   PetscCall(ISGetLocalSize(valueIS, &Nct));
98   PetscCall(ISGetIndices(valueIS, &ctypes));
99   for (PetscInt t = 0; t < Nct; ++t) {
100     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
101     PetscInt             ctS, ctE, ht;
102 
103     if (ct == DM_POLYTOPE_UNKNOWN) {
104       // If any cells are not typed, just use all cells
105       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
106       break;
107     }
108     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
109     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
110     if (ctS >= ctE) continue;
111     // Check that a point has the right height
112     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
113     if (ht != height) continue;
114     cS    = PetscMin(cS, ctS);
115     cE    = PetscMax(cE, ctE);
116     found = PETSC_TRUE;
117   }
118   if (!Nct || !found) cS = cE = 0;
119   PetscCall(ISDestroy(&valueIS));
120   // Reset label for fast lookup
121   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
122   if (cStart) *cStart = cS;
123   if (cEnd) *cEnd = cE;
124   PetscFunctionReturn(PETSC_SUCCESS);
125 }
126 
127 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
128 {
129   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
130   PetscInt                *sStart, *sEnd;
131   PetscViewerVTKFieldType *ft;
132   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
133   DMLabel                  depthLabel, ctLabel;
134 
135   PetscFunctionBegin;
136   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
137   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
138   PetscCall(DMGetCoordinateDim(dm, &cdim));
139   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
140   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
141   if (field >= 0) {
142     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
143   } else {
144     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
145   }
146 
147   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
148   PetscCall(DMPlexGetDepth(dm, &depth));
149   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
150   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
151   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
152     const DMPolytopeType ict = (DMPolytopeType)c;
153     PetscInt             dep;
154 
155     if (ict == DM_POLYTOPE_FV_GHOST) continue;
156     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
157     if (pStart >= 0) {
158       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
159       if (dep != depth - cellHeight) continue;
160     }
161     if (field >= 0) {
162       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
163     } else {
164       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
165     }
166   }
167 
168   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
169   *types = 0;
170 
171   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
172     if (globalvcdof[c]) ++(*types);
173   }
174 
175   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
176   t = 0;
177   if (globalvcdof[DM_NUM_POLYTOPES]) {
178     sStart[t] = vStart;
179     sEnd[t]   = vEnd;
180     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
181     ++t;
182   }
183 
184   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
185     if (globalvcdof[c]) {
186       const DMPolytopeType ict = (DMPolytopeType)c;
187 
188       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
189       sStart[t] = cStart;
190       sEnd[t]   = cEnd;
191       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
192       ++t;
193     }
194   }
195 
196   if (!*types) {
197     if (field >= 0) {
198       const char *fieldname;
199 
200       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
201       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
202     } else {
203       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
204     }
205   }
206 
207   *ssStart = sStart;
208   *ssEnd   = sEnd;
209   *sft     = ft;
210   PetscFunctionReturn(PETSC_SUCCESS);
211 }
212 
213 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
214 {
215   PetscFunctionBegin;
216   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
221 {
222   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
223   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
224 
225   PetscFunctionBegin;
226   *ft = PETSC_VTK_INVALID;
227   PetscCall(DMGetCoordinateDim(dm, &cdim));
228   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
229   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
230   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
231   if (field >= 0) {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
234   } else {
235     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
236     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
237   }
238   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
239   if (globalvcdof[0]) {
240     *sStart = vStart;
241     *sEnd   = vEnd;
242     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
243     else *ft = PETSC_VTK_POINT_FIELD;
244   } else if (globalvcdof[1]) {
245     *sStart = cStart;
246     *sEnd   = cEnd;
247     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
248     else *ft = PETSC_VTK_CELL_FIELD;
249   } else {
250     if (field >= 0) {
251       const char *fieldname;
252 
253       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
254       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
255     } else {
256       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section\n"));
257     }
258   }
259   PetscFunctionReturn(PETSC_SUCCESS);
260 }
261 
262 /*@
263   DMPlexVecView1D - Plot many 1D solutions on the same line graph
264 
265   Collective
266 
267   Input Parameters:
268 + dm     - The `DMPLEX` object
269 . n      - The number of vectors
270 . u      - The array of local vectors
271 - viewer - The `PetscViewer`
272 
273   Level: advanced
274 
275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
276 @*/
277 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
278 {
279   DM                 cdm;
280   PetscDS            ds;
281   PetscDraw          draw = NULL;
282   PetscDrawLG        lg;
283   Vec                coordinates;
284   const PetscScalar *coords, **sol;
285   PetscReal         *vals;
286   PetscInt          *Nc;
287   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
288   char             **names;
289 
290   PetscFunctionBegin;
291   PetscCall(DMGetCoordinateDM(dm, &cdm));
292   PetscCall(DMGetDS(dm, &ds));
293   PetscCall(PetscDSGetNumFields(ds, &Nf));
294   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
295   PetscCall(PetscDSGetComponents(ds, &Nc));
296 
297   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
298   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
299   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
300 
301   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
302   for (PetscInt i = 0, l = 0; i < n; ++i) {
303     const char *vname;
304 
305     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
306     for (PetscInt f = 0; f < Nf; ++f) {
307       PetscObject disc;
308       const char *fname;
309       char        tmpname[PETSC_MAX_PATH_LEN];
310 
311       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
312       /* TODO Create names for components */
313       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
314         PetscCall(PetscObjectGetName(disc, &fname));
315         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
316         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
317         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
318         PetscCall(PetscStrallocpy(tmpname, &names[l]));
319       }
320     }
321   }
322   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
323   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
324   PetscCall(VecGetArrayRead(coordinates, &coords));
325   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
326   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
327   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
328   PetscSection s;
329   PetscInt     cdof, vdof;
330 
331   PetscCall(DMGetLocalSection(dm, &s));
332   PetscCall(PetscSectionGetDof(s, eStart, &cdof));
333   PetscCall(PetscSectionGetDof(s, vStart, &vdof));
334   if (cdof) {
335     if (vdof) {
336       // P_2
337       PetscInt vFirst = -1;
338 
339       for (PetscInt e = eStart; e < eEnd; ++e) {
340         PetscScalar    *xa, *xb, *svals;
341         const PetscInt *cone;
342 
343         PetscCall(DMPlexGetCone(dm, e, &cone));
344         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
345         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
346         if (e == eStart) vFirst = cone[0];
347         for (PetscInt i = 0; i < n; ++i) {
348           PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
349           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
350         }
351         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
352         if (e == eEnd - 1 && cone[1] != vFirst) {
353           for (PetscInt i = 0; i < n; ++i) {
354             PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
355             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
356           }
357           PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
358           for (PetscInt i = 0; i < n; ++i) {
359             PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
360             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
361           }
362           PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
363         }
364       }
365     } else {
366       // P_0
367       for (PetscInt e = eStart; e < eEnd; ++e) {
368         PetscScalar    *xa, *xb, *svals;
369         const PetscInt *cone;
370 
371         PetscCall(DMPlexGetCone(dm, e, &cone));
372         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
373         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
374         for (PetscInt i = 0; i < n; ++i) {
375           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
376           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
377         }
378         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
379       }
380     }
381   } else if (vdof) {
382     // P_1
383     for (PetscInt v = vStart; v < vEnd; ++v) {
384       PetscScalar *x, *svals;
385 
386       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
387       for (PetscInt i = 0; i < n; ++i) {
388         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
389         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
390       }
391       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
392     }
393   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Discretization not supported");
394   PetscCall(VecRestoreArrayRead(coordinates, &coords));
395   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
396   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
397   PetscCall(PetscFree3(sol, names, vals));
398 
399   PetscCall(PetscDrawLGDraw(lg));
400   PetscCall(PetscDrawLGDestroy(&lg));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
405 {
406   DM dm;
407 
408   PetscFunctionBegin;
409   PetscCall(VecGetDM(u, &dm));
410   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
411   PetscFunctionReturn(PETSC_SUCCESS);
412 }
413 
414 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
415 {
416   DM                 dm;
417   PetscSection       s;
418   PetscDraw          draw, popup;
419   DM                 cdm;
420   PetscSection       coordSection;
421   Vec                coordinates;
422   const PetscScalar *array;
423   PetscReal          lbound[3], ubound[3];
424   PetscReal          vbound[2], time;
425   PetscBool          flg;
426   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
427   const char        *name;
428   char               title[PETSC_MAX_PATH_LEN];
429 
430   PetscFunctionBegin;
431   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
432   PetscCall(VecGetDM(v, &dm));
433   PetscCall(DMGetCoordinateDim(dm, &dim));
434   PetscCall(DMGetLocalSection(dm, &s));
435   PetscCall(PetscSectionGetNumFields(s, &Nf));
436   PetscCall(DMGetCoarsenLevel(dm, &level));
437   PetscCall(DMGetCoordinateDM(dm, &cdm));
438   PetscCall(DMGetLocalSection(cdm, &coordSection));
439   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
440   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
441   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
442 
443   PetscCall(PetscObjectGetName((PetscObject)v, &name));
444   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
445 
446   PetscCall(VecGetLocalSize(coordinates, &N));
447   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
448   PetscCall(PetscDrawClear(draw));
449 
450   /* Could implement something like DMDASelectFields() */
451   for (f = 0; f < Nf; ++f) {
452     DM          fdm = dm;
453     Vec         fv  = v;
454     IS          fis;
455     char        prefix[PETSC_MAX_PATH_LEN];
456     const char *fname;
457 
458     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
459     PetscCall(PetscSectionGetFieldName(s, f, &fname));
460 
461     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
462     else prefix[0] = '\0';
463     if (Nf > 1) {
464       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
465       PetscCall(VecGetSubVector(v, fis, &fv));
466       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
467       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
468     }
469     for (comp = 0; comp < Nc; ++comp, ++w) {
470       PetscInt nmax = 2;
471 
472       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
473       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
474       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
475       PetscCall(PetscDrawSetTitle(draw, title));
476 
477       /* TODO Get max and min only for this component */
478       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
479       if (!flg) {
480         PetscCall(VecMin(fv, NULL, &vbound[0]));
481         PetscCall(VecMax(fv, NULL, &vbound[1]));
482         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
483       }
484 
485       PetscCall(PetscDrawGetPopup(draw, &popup));
486       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
487       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
488       PetscCall(VecGetArrayRead(fv, &array));
489       for (c = cStart; c < cEnd; ++c) {
490         DMPolytopeType     ct;
491         PetscScalar       *coords = NULL, *a = NULL;
492         const PetscScalar *coords_arr;
493         PetscBool          isDG;
494         PetscInt           numCoords;
495         int                color[4] = {-1, -1, -1, -1};
496 
497         PetscCall(DMPlexGetCellType(dm, c, &ct));
498         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
499         if (a) {
500           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
501           color[1] = color[2] = color[3] = color[0];
502         } else {
503           PetscScalar *vals = NULL;
504           PetscInt     numVals, va;
505 
506           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
507           if (!numVals) {
508             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
509             continue;
510           }
511           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
512           switch (numVals / Nc) {
513           case 1: /* P1 Clamped Segment Prism */
514           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
515             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
516             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
517             break;
518           case 3: /* P1 Triangle */
519           case 4: /* P1 Quadrangle */
520             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
521             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
522             break;
523           case 6: /* P2 Triangle */
524           case 8: /* P2 Quadrangle */
525             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
526             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
527             break;
528           default:
529             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
530           }
531           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
532         }
533         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
534         switch (numCoords) {
535         case 6:
536         case 12: /* Localized triangle */
537           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
538           break;
539         case 8:
540         case 16: /* Localized quadrilateral */
541           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
542             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
543           } else {
544             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
545             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
546           }
547           break;
548         default:
549           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
550         }
551         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
552       }
553       PetscCall(VecRestoreArrayRead(fv, &array));
554       PetscCall(PetscDrawFlush(draw));
555       PetscCall(PetscDrawPause(draw));
556       PetscCall(PetscDrawSave(draw));
557     }
558     if (Nf > 1) {
559       PetscCall(VecRestoreSubVector(v, fis, &fv));
560       PetscCall(ISDestroy(&fis));
561       PetscCall(DMDestroy(&fdm));
562     }
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscDraw draw;
571   PetscInt  dim;
572   PetscBool isnull;
573 
574   PetscFunctionBegin;
575   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
576   PetscCall(PetscDrawIsNull(draw, &isnull));
577   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
578 
579   PetscCall(VecGetDM(v, &dm));
580   PetscCall(DMGetCoordinateDim(dm, &dim));
581   switch (dim) {
582   case 1:
583     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
584     break;
585   case 2:
586     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
587     break;
588   default:
589     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
590   }
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
595 {
596   DM                      dm;
597   Vec                     locv;
598   const char             *name;
599   PetscSection            section;
600   PetscInt                pStart, pEnd;
601   PetscInt                numFields;
602   PetscViewerVTKFieldType ft;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
607   PetscCall(PetscObjectGetName((PetscObject)v, &name));
608   PetscCall(PetscObjectSetName((PetscObject)locv, name));
609   PetscCall(VecCopy(v, locv));
610   PetscCall(DMGetLocalSection(dm, &section));
611   PetscCall(PetscSectionGetNumFields(section, &numFields));
612   if (!numFields) {
613     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
614     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
615   } else {
616     PetscInt f;
617 
618     for (f = 0; f < numFields; f++) {
619       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
620       if (ft == PETSC_VTK_INVALID) continue;
621       PetscCall(PetscObjectReference((PetscObject)locv));
622       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
623     }
624     PetscCall(VecDestroy(&locv));
625   }
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
641   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
642   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
643   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
644     PetscInt    i, numFields;
645     PetscObject fe;
646     PetscBool   fem  = PETSC_FALSE;
647     Vec         locv = v;
648     const char *name;
649     PetscInt    step;
650     PetscReal   time;
651 
652     PetscCall(DMGetNumFields(dm, &numFields));
653     for (i = 0; i < numFields; i++) {
654       PetscCall(DMGetField(dm, i, NULL, &fe));
655       if (fe->classid == PETSCFE_CLASSID) {
656         fem = PETSC_TRUE;
657         break;
658       }
659     }
660     if (fem) {
661       PetscObject isZero;
662 
663       PetscCall(DMGetLocalVector(dm, &locv));
664       PetscCall(PetscObjectGetName((PetscObject)v, &name));
665       PetscCall(PetscObjectSetName((PetscObject)locv, name));
666       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
667       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
668       PetscCall(VecCopy(v, locv));
669       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
670       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
671     }
672     if (isvtk) {
673       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
674     } else if (ishdf5) {
675 #if defined(PETSC_HAVE_HDF5)
676       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
677 #else
678       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680     } else if (isdraw) {
681       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
682     } else if (ispython) {
683       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
684     } else if (isglvis) {
685       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
686       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
687       PetscCall(VecView_GLVis(locv, viewer));
688     } else if (iscgns) {
689 #if defined(PETSC_HAVE_CGNS)
690       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
691 #else
692       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
693 #endif
694     }
695     if (fem) {
696       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
697       PetscCall(DMRestoreLocalVector(dm, &locv));
698     }
699   } else {
700     PetscBool isseq;
701 
702     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
703     if (isseq) PetscCall(VecView_Seq(v, viewer));
704     else PetscCall(VecView_MPI(v, viewer));
705   }
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
710 {
711   DM        dm;
712   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
713 
714   PetscFunctionBegin;
715   PetscCall(VecGetDM(v, &dm));
716   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
723   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
724   if (isvtk || isdraw || isglvis || iscgns || ispython) {
725     Vec         locv;
726     PetscObject isZero;
727     const char *name;
728 
729     PetscCall(DMGetLocalVector(dm, &locv));
730     PetscCall(PetscObjectGetName((PetscObject)v, &name));
731     PetscCall(PetscObjectSetName((PetscObject)locv, name));
732     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
733     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
734     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
735     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
736     PetscCall(VecView_Plex_Local(locv, viewer));
737     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
738     PetscCall(DMRestoreLocalVector(dm, &locv));
739   } else if (ishdf5) {
740 #if defined(PETSC_HAVE_HDF5)
741     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
742 #else
743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
744 #endif
745   } else if (isexodusii) {
746 #if defined(PETSC_HAVE_EXODUSII)
747     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
748 #else
749     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
750 #endif
751   } else {
752     PetscBool isseq;
753 
754     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
755     if (isseq) PetscCall(VecView_Seq(v, viewer));
756     else PetscCall(VecView_MPI(v, viewer));
757   }
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
762 {
763   DM                dm;
764   MPI_Comm          comm;
765   PetscViewerFormat format;
766   Vec               v;
767   PetscBool         isvtk, ishdf5;
768 
769   PetscFunctionBegin;
770   PetscCall(VecGetDM(originalv, &dm));
771   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
772   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
773   PetscCall(PetscViewerGetFormat(viewer, &format));
774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
776   if (format == PETSC_VIEWER_NATIVE) {
777     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
778     /* this need a better fix */
779     if (dm->useNatural) {
780       if (dm->sfNatural) {
781         const char *vecname;
782         PetscInt    n, nroots;
783 
784         PetscCall(VecGetLocalSize(originalv, &n));
785         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
786         if (n == nroots) {
787           PetscCall(DMPlexCreateNaturalVector(dm, &v));
788           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
789           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
790           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
791           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
792         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
793       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
794     } else v = originalv;
795   } else v = originalv;
796 
797   if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
800 #else
801     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
802 #endif
803   } else if (isvtk) {
804     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
805   } else {
806     PetscBool isseq;
807 
808     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
809     if (isseq) PetscCall(VecView_Seq(v, viewer));
810     else PetscCall(VecView_MPI(v, viewer));
811   }
812   if (v != originalv) PetscCall(VecDestroy(&v));
813   PetscFunctionReturn(PETSC_SUCCESS);
814 }
815 
816 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
817 {
818   DM        dm;
819   PetscBool ishdf5;
820 
821   PetscFunctionBegin;
822   PetscCall(VecGetDM(v, &dm));
823   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
824   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
825   if (ishdf5) {
826     DM          dmBC;
827     Vec         gv;
828     const char *name;
829 
830     PetscCall(DMGetOutputDM(dm, &dmBC));
831     PetscCall(DMGetGlobalVector(dmBC, &gv));
832     PetscCall(PetscObjectGetName((PetscObject)v, &name));
833     PetscCall(PetscObjectSetName((PetscObject)gv, name));
834     PetscCall(VecLoad_Default(gv, viewer));
835     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
836     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
837     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
838   } else PetscCall(VecLoad_Default(v, viewer));
839   PetscFunctionReturn(PETSC_SUCCESS);
840 }
841 
842 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
843 {
844   DM        dm;
845   PetscBool ishdf5, isexodusii, iscgns;
846 
847   PetscFunctionBegin;
848   PetscCall(VecGetDM(v, &dm));
849   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
850   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
851   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
852   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
853   if (ishdf5) {
854 #if defined(PETSC_HAVE_HDF5)
855     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
856 #else
857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
858 #endif
859   } else if (isexodusii) {
860 #if defined(PETSC_HAVE_EXODUSII)
861     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
862 #else
863     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
864 #endif
865   } else if (iscgns) {
866 #if defined(PETSC_HAVE_CGNS)
867     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
868 #else
869     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
870 #endif
871   } else PetscCall(VecLoad_Default(v, viewer));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
876 {
877   DM                dm;
878   PetscViewerFormat format;
879   PetscBool         ishdf5;
880 
881   PetscFunctionBegin;
882   PetscCall(VecGetDM(originalv, &dm));
883   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
884   PetscCall(PetscViewerGetFormat(viewer, &format));
885   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
886   if (format == PETSC_VIEWER_NATIVE) {
887     if (dm->useNatural) {
888       if (dm->sfNatural) {
889         if (ishdf5) {
890 #if defined(PETSC_HAVE_HDF5)
891           Vec         v;
892           const char *vecname;
893 
894           PetscCall(DMPlexCreateNaturalVector(dm, &v));
895           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
896           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
897           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
898           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
899           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
900           PetscCall(VecDestroy(&v));
901 #else
902           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
903 #endif
904         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
905       }
906     } else PetscCall(VecLoad_Default(originalv, viewer));
907   }
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
912 {
913   PetscSection       coordSection;
914   Vec                coordinates;
915   DMLabel            depthLabel, celltypeLabel;
916   const char        *name[4];
917   const PetscScalar *a;
918   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
919 
920   PetscFunctionBegin;
921   PetscCall(DMGetDimension(dm, &dim));
922   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
923   PetscCall(DMGetCoordinateSection(dm, &coordSection));
924   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
925   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
926   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
927   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
928   PetscCall(VecGetArrayRead(coordinates, &a));
929   name[0]       = "vertex";
930   name[1]       = "edge";
931   name[dim - 1] = "face";
932   name[dim]     = "cell";
933   for (c = cStart; c < cEnd; ++c) {
934     PetscInt *closure = NULL;
935     PetscInt  closureSize, cl, ct;
936 
937     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
938     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
939     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
940     PetscCall(PetscViewerASCIIPushTab(viewer));
941     for (cl = 0; cl < closureSize * 2; cl += 2) {
942       PetscInt point = closure[cl], depth, dof, off, d, p;
943 
944       if ((point < pStart) || (point >= pEnd)) continue;
945       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
946       if (!dof) continue;
947       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
948       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
949       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
950       for (p = 0; p < dof / dim; ++p) {
951         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
952         for (d = 0; d < dim; ++d) {
953           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
954           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
955         }
956         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
957       }
958       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
959     }
960     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
961     PetscCall(PetscViewerASCIIPopTab(viewer));
962   }
963   PetscCall(VecRestoreArrayRead(coordinates, &a));
964   PetscFunctionReturn(PETSC_SUCCESS);
965 }
966 
967 typedef enum {
968   CS_CARTESIAN,
969   CS_POLAR,
970   CS_CYLINDRICAL,
971   CS_SPHERICAL
972 } CoordSystem;
973 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
974 
975 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
976 {
977   PetscInt i;
978 
979   PetscFunctionBegin;
980   if (dim > 3) {
981     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
982   } else {
983     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
984 
985     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
986     switch (cs) {
987     case CS_CARTESIAN:
988       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
989       break;
990     case CS_POLAR:
991       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
992       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
993       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
994       break;
995     case CS_CYLINDRICAL:
996       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
997       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
998       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
999       trcoords[2] = coords[2];
1000       break;
1001     case CS_SPHERICAL:
1002       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
1003       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1004       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1005       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1006       break;
1007     }
1008     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1009   }
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1014 {
1015   DM_Plex          *mesh = (DM_Plex *)dm->data;
1016   DM                cdm, cdmCell;
1017   PetscSection      coordSection, coordSectionCell;
1018   Vec               coordinates, coordinatesCell;
1019   PetscViewerFormat format;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscViewerGetFormat(viewer, &format));
1023   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1024     const char *name;
1025     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1026     PetscInt    pStart, pEnd, p, numLabels, l;
1027     PetscMPIInt rank, size;
1028 
1029     PetscCall(DMGetCoordinateDM(dm, &cdm));
1030     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1031     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1032     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1033     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1034     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1035     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1036     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1037     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1039     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1040     PetscCall(DMGetDimension(dm, &dim));
1041     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1042     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1043     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1044     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1045     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1046     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1047     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1048     for (p = pStart; p < pEnd; ++p) {
1049       PetscInt dof, off, s;
1050 
1051       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1052       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1053       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1054     }
1055     PetscCall(PetscViewerFlush(viewer));
1056     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1057     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1058     for (p = pStart; p < pEnd; ++p) {
1059       PetscInt dof, off, c;
1060 
1061       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1062       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1063       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
1064     }
1065     PetscCall(PetscViewerFlush(viewer));
1066     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1067     if (coordSection && coordinates) {
1068       CoordSystem        cs = CS_CARTESIAN;
1069       const PetscScalar *array, *arrayCell = NULL;
1070       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1071       PetscMPIInt        rank;
1072       const char        *name;
1073 
1074       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1075       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1076       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1077       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1078       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1079       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1080       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1081       pStart = PetscMin(pvStart, pcStart);
1082       pEnd   = PetscMax(pvEnd, pcEnd);
1083       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1084       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1085       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1086       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1087 
1088       PetscCall(VecGetArrayRead(coordinates, &array));
1089       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1090       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1091       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1092       for (p = pStart; p < pEnd; ++p) {
1093         PetscInt dof, off;
1094 
1095         if (p >= pvStart && p < pvEnd) {
1096           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1097           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1098           if (dof) {
1099             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1100             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1101             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1102           }
1103         }
1104         if (cdmCell && p >= pcStart && p < pcEnd) {
1105           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1106           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1107           if (dof) {
1108             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1109             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1110             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1111           }
1112         }
1113       }
1114       PetscCall(PetscViewerFlush(viewer));
1115       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1116       PetscCall(VecRestoreArrayRead(coordinates, &array));
1117       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1118     }
1119     PetscCall(DMGetNumLabels(dm, &numLabels));
1120     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1121     for (l = 0; l < numLabels; ++l) {
1122       DMLabel     label;
1123       PetscBool   isdepth;
1124       const char *name;
1125 
1126       PetscCall(DMGetLabelName(dm, l, &name));
1127       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1128       if (isdepth) continue;
1129       PetscCall(DMGetLabel(dm, name, &label));
1130       PetscCall(DMLabelView(label, viewer));
1131     }
1132     if (size > 1) {
1133       PetscSF sf;
1134 
1135       PetscCall(DMGetPointSF(dm, &sf));
1136       PetscCall(PetscSFView(sf, viewer));
1137     }
1138     if (mesh->periodic.face_sfs)
1139       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1140     PetscCall(PetscViewerFlush(viewer));
1141   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1142     const char  *name, *color;
1143     const char  *defcolors[3]  = {"gray", "orange", "green"};
1144     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1145     char         lname[PETSC_MAX_PATH_LEN];
1146     PetscReal    scale      = 2.0;
1147     PetscReal    tikzscale  = 1.0;
1148     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1149     double       tcoords[3];
1150     PetscScalar *coords;
1151     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1152     PetscMPIInt  rank, size;
1153     char       **names, **colors, **lcolors;
1154     PetscBool    flg, lflg;
1155     PetscBT      wp = NULL;
1156     PetscInt     pEnd, pStart;
1157 
1158     PetscCall(DMGetCoordinateDM(dm, &cdm));
1159     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1160     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1161     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1162     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1163     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1164     PetscCall(DMGetDimension(dm, &dim));
1165     PetscCall(DMPlexGetDepth(dm, &depth));
1166     PetscCall(DMGetNumLabels(dm, &numLabels));
1167     numLabels  = PetscMax(numLabels, 10);
1168     numColors  = 10;
1169     numLColors = 10;
1170     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1171     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1172     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1173     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1174     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1175     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1176     n = 4;
1177     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1178     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1179     n = 4;
1180     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1181     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1182     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1183     if (!useLabels) numLabels = 0;
1184     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1185     if (!useColors) {
1186       numColors = 3;
1187       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1188     }
1189     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1190     if (!useColors) {
1191       numLColors = 4;
1192       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1193     }
1194     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1195     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1196     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1197     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1198     if (depth < dim) plotEdges = PETSC_FALSE;
1199     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1200 
1201     /* filter points with labelvalue != labeldefaultvalue */
1202     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1203     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1204     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1205     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1206     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1207     if (lflg) {
1208       DMLabel lbl;
1209 
1210       PetscCall(DMGetLabel(dm, lname, &lbl));
1211       if (lbl) {
1212         PetscInt val, defval;
1213 
1214         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1215         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1216         for (c = pStart; c < pEnd; c++) {
1217           PetscInt *closure = NULL;
1218           PetscInt  closureSize;
1219 
1220           PetscCall(DMLabelGetValue(lbl, c, &val));
1221           if (val == defval) continue;
1222 
1223           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229 
1230     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1231     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1232     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1233     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1234 \\documentclass[tikz]{standalone}\n\n\
1235 \\usepackage{pgflibraryshapes}\n\
1236 \\usetikzlibrary{backgrounds}\n\
1237 \\usetikzlibrary{arrows}\n\
1238 \\begin{document}\n"));
1239     if (size > 1) {
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1241       for (p = 0; p < size; ++p) {
1242         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1243         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1244       }
1245       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1246     }
1247     if (drawHasse) {
1248       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1249 
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1264       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1265       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1266     }
1267     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1268 
1269     /* Plot vertices */
1270     PetscCall(VecGetArray(coordinates, &coords));
1271     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1272     for (v = vStart; v < vEnd; ++v) {
1273       PetscInt  off, dof, d;
1274       PetscBool isLabeled = PETSC_FALSE;
1275 
1276       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1277       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1278       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1279       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1280       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1281       for (d = 0; d < dof; ++d) {
1282         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1283         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1284       }
1285       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1286       if (dim == 3) {
1287         PetscReal tmp = tcoords[1];
1288         tcoords[1]    = tcoords[2];
1289         tcoords[2]    = -tmp;
1290       }
1291       for (d = 0; d < dof; ++d) {
1292         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1293         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1294       }
1295       if (drawHasse) color = colors[0 % numColors];
1296       else color = colors[rank % numColors];
1297       for (l = 0; l < numLabels; ++l) {
1298         PetscInt val;
1299         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1300         if (val >= 0) {
1301           color     = lcolors[l % numLColors];
1302           isLabeled = PETSC_TRUE;
1303           break;
1304         }
1305       }
1306       if (drawNumbers[0]) {
1307         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1308       } else if (drawColors[0]) {
1309         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1310       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1311     }
1312     PetscCall(VecRestoreArray(coordinates, &coords));
1313     PetscCall(PetscViewerFlush(viewer));
1314     /* Plot edges */
1315     if (plotEdges) {
1316       PetscCall(VecGetArray(coordinates, &coords));
1317       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1318       for (e = eStart; e < eEnd; ++e) {
1319         const PetscInt *cone;
1320         PetscInt        coneSize, offA, offB, dof, d;
1321 
1322         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1323         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1324         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1325         PetscCall(DMPlexGetCone(dm, e, &cone));
1326         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1327         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1328         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1329         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1330         for (d = 0; d < dof; ++d) {
1331           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1332           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1333         }
1334         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1335         if (dim == 3) {
1336           PetscReal tmp = tcoords[1];
1337           tcoords[1]    = tcoords[2];
1338           tcoords[2]    = -tmp;
1339         }
1340         for (d = 0; d < dof; ++d) {
1341           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1342           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1343         }
1344         if (drawHasse) color = colors[1 % numColors];
1345         else color = colors[rank % numColors];
1346         for (l = 0; l < numLabels; ++l) {
1347           PetscInt val;
1348           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1349           if (val >= 0) {
1350             color = lcolors[l % numLColors];
1351             break;
1352           }
1353         }
1354         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1355       }
1356       PetscCall(VecRestoreArray(coordinates, &coords));
1357       PetscCall(PetscViewerFlush(viewer));
1358       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1359     }
1360     /* Plot cells */
1361     if (dim == 3 || !drawNumbers[1]) {
1362       for (e = eStart; e < eEnd; ++e) {
1363         const PetscInt *cone;
1364 
1365         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1366         color = colors[rank % numColors];
1367         for (l = 0; l < numLabels; ++l) {
1368           PetscInt val;
1369           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1370           if (val >= 0) {
1371             color = lcolors[l % numLColors];
1372             break;
1373           }
1374         }
1375         PetscCall(DMPlexGetCone(dm, e, &cone));
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1377       }
1378     } else {
1379       DMPolytopeType ct;
1380 
1381       /* Drawing a 2D polygon */
1382       for (c = cStart; c < cEnd; ++c) {
1383         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1384         PetscCall(DMPlexGetCellType(dm, c, &ct));
1385         if (DMPolytopeTypeIsHybrid(ct)) {
1386           const PetscInt *cone;
1387           PetscInt        coneSize, e;
1388 
1389           PetscCall(DMPlexGetCone(dm, c, &cone));
1390           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1391           for (e = 0; e < coneSize; ++e) {
1392             const PetscInt *econe;
1393 
1394             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1395             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1396           }
1397         } else {
1398           PetscInt *closure = NULL;
1399           PetscInt  closureSize, Nv = 0, v;
1400 
1401           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1402           for (p = 0; p < closureSize * 2; p += 2) {
1403             const PetscInt point = closure[p];
1404 
1405             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1406           }
1407           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1408           for (v = 0; v <= Nv; ++v) {
1409             const PetscInt vertex = closure[v % Nv];
1410 
1411             if (v > 0) {
1412               if (plotEdges) {
1413                 const PetscInt *edge;
1414                 PetscInt        endpoints[2], ne;
1415 
1416                 endpoints[0] = closure[v - 1];
1417                 endpoints[1] = vertex;
1418                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1419                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1420                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1421                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1422               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1423             }
1424             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1425           }
1426           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1427           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1428         }
1429       }
1430     }
1431     for (c = cStart; c < cEnd; ++c) {
1432       double             ccoords[3] = {0.0, 0.0, 0.0};
1433       PetscBool          isLabeled  = PETSC_FALSE;
1434       PetscScalar       *cellCoords = NULL;
1435       const PetscScalar *array;
1436       PetscInt           numCoords, cdim, d;
1437       PetscBool          isDG;
1438 
1439       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1440       PetscCall(DMGetCoordinateDim(dm, &cdim));
1441       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1442       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1443       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1444       for (p = 0; p < numCoords / cdim; ++p) {
1445         for (d = 0; d < cdim; ++d) {
1446           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1447           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1448         }
1449         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1450         if (cdim == 3) {
1451           PetscReal tmp = tcoords[1];
1452           tcoords[1]    = tcoords[2];
1453           tcoords[2]    = -tmp;
1454         }
1455         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1456       }
1457       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1458       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1459       for (d = 0; d < cdim; ++d) {
1460         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1461         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1462       }
1463       if (drawHasse) color = colors[depth % numColors];
1464       else color = colors[rank % numColors];
1465       for (l = 0; l < numLabels; ++l) {
1466         PetscInt val;
1467         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1468         if (val >= 0) {
1469           color     = lcolors[l % numLColors];
1470           isLabeled = PETSC_TRUE;
1471           break;
1472         }
1473       }
1474       if (drawNumbers[dim]) {
1475         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1476       } else if (drawColors[dim]) {
1477         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1478       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1479     }
1480     if (drawHasse) {
1481       int height = 0;
1482 
1483       color = colors[depth % numColors];
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1485       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1487       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1489 
1490       if (depth > 2) {
1491         color = colors[1 % numColors];
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1496         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1497       }
1498 
1499       color = colors[1 % numColors];
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1501       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1502       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1503       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1504       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1505 
1506       color = colors[0 % numColors];
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1508       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1509       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1510       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1511       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1512 
1513       for (p = pStart; p < pEnd; ++p) {
1514         const PetscInt *cone;
1515         PetscInt        coneSize, cp;
1516 
1517         PetscCall(DMPlexGetCone(dm, p, &cone));
1518         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1519         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1520       }
1521     }
1522     PetscCall(PetscViewerFlush(viewer));
1523     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1524     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1526     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1527     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1528     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1529     PetscCall(PetscFree3(names, colors, lcolors));
1530     PetscCall(PetscBTDestroy(&wp));
1531   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1532     Vec                    cown, acown;
1533     VecScatter             sct;
1534     ISLocalToGlobalMapping g2l;
1535     IS                     gid, acis;
1536     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1537     MPI_Group              ggroup, ngroup;
1538     PetscScalar           *array, nid;
1539     const PetscInt        *idxs;
1540     PetscInt              *idxs2, *start, *adjacency, *work;
1541     PetscInt64             lm[3], gm[3];
1542     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1543     PetscMPIInt            d1, d2, rank;
1544 
1545     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1546     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1547 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1548     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1549 #endif
1550     if (ncomm != MPI_COMM_NULL) {
1551       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1552       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1553       d1 = 0;
1554       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1555       nid = d2;
1556       PetscCallMPI(MPI_Group_free(&ggroup));
1557       PetscCallMPI(MPI_Group_free(&ngroup));
1558       PetscCallMPI(MPI_Comm_free(&ncomm));
1559     } else nid = 0.0;
1560 
1561     /* Get connectivity */
1562     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1563     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1564 
1565     /* filter overlapped local cells */
1566     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1567     PetscCall(ISGetIndices(gid, &idxs));
1568     PetscCall(ISGetLocalSize(gid, &cum));
1569     PetscCall(PetscMalloc1(cum, &idxs2));
1570     for (c = cStart, cum = 0; c < cEnd; c++) {
1571       if (idxs[c - cStart] < 0) continue;
1572       idxs2[cum++] = idxs[c - cStart];
1573     }
1574     PetscCall(ISRestoreIndices(gid, &idxs));
1575     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1576     PetscCall(ISDestroy(&gid));
1577     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1578 
1579     /* support for node-aware cell locality */
1580     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1581     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1582     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1583     PetscCall(VecGetArray(cown, &array));
1584     for (c = 0; c < numVertices; c++) array[c] = nid;
1585     PetscCall(VecRestoreArray(cown, &array));
1586     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1587     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1588     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1589     PetscCall(ISDestroy(&acis));
1590     PetscCall(VecScatterDestroy(&sct));
1591     PetscCall(VecDestroy(&cown));
1592 
1593     /* compute edgeCut */
1594     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1595     PetscCall(PetscMalloc1(cum, &work));
1596     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1597     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1598     PetscCall(ISDestroy(&gid));
1599     PetscCall(VecGetArray(acown, &array));
1600     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1601       PetscInt totl;
1602 
1603       totl = start[c + 1] - start[c];
1604       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1605       for (i = 0; i < totl; i++) {
1606         if (work[i] < 0) {
1607           ect += 1;
1608           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1609         }
1610       }
1611     }
1612     PetscCall(PetscFree(work));
1613     PetscCall(VecRestoreArray(acown, &array));
1614     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1615     lm[1] = -numVertices;
1616     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1617     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1618     lm[0] = ect;                     /* edgeCut */
1619     lm[1] = ectn;                    /* node-aware edgeCut */
1620     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1621     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1622     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1623 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1624     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1625 #else
1626     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1627 #endif
1628     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1629     PetscCall(PetscFree(start));
1630     PetscCall(PetscFree(adjacency));
1631     PetscCall(VecDestroy(&acown));
1632   } else {
1633     const char    *name;
1634     PetscInt      *sizes, *hybsizes, *ghostsizes;
1635     PetscInt       locDepth, depth, cellHeight, dim, d;
1636     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1637     PetscInt       numLabels, l, maxSize = 17;
1638     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1639     MPI_Comm       comm;
1640     PetscMPIInt    size, rank;
1641 
1642     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1643     PetscCallMPI(MPI_Comm_size(comm, &size));
1644     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1645     PetscCall(DMGetDimension(dm, &dim));
1646     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1647     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1648     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1649     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1650     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1651     PetscCall(DMPlexGetDepth(dm, &locDepth));
1652     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1653     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1654     gcNum = gcEnd - gcStart;
1655     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1656     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1657     for (d = 0; d <= depth; d++) {
1658       PetscInt Nc[2] = {0, 0}, ict;
1659 
1660       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1661       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1662       ict = ct0;
1663       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1664       ct0 = (DMPolytopeType)ict;
1665       for (p = pStart; p < pEnd; ++p) {
1666         DMPolytopeType ct;
1667 
1668         PetscCall(DMPlexGetCellType(dm, p, &ct));
1669         if (ct == ct0) ++Nc[0];
1670         else ++Nc[1];
1671       }
1672       if (size < maxSize) {
1673         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1674         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1675         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1676         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1677         for (p = 0; p < size; ++p) {
1678           if (rank == 0) {
1679             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1680             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1681             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1682           }
1683         }
1684       } else {
1685         PetscInt locMinMax[2];
1686 
1687         locMinMax[0] = Nc[0] + Nc[1];
1688         locMinMax[1] = Nc[0] + Nc[1];
1689         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1690         locMinMax[0] = Nc[1];
1691         locMinMax[1] = Nc[1];
1692         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1693         if (d == depth) {
1694           locMinMax[0] = gcNum;
1695           locMinMax[1] = gcNum;
1696           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1697         }
1698         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1699         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1700         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1701         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1702       }
1703       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1704     }
1705     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1706     {
1707       const PetscReal *maxCell;
1708       const PetscReal *L;
1709       PetscBool        localized;
1710 
1711       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1712       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1713       if (L || localized) {
1714         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1715         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1716         if (L) {
1717           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1718           for (d = 0; d < dim; ++d) {
1719             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1720             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1721           }
1722           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1723         }
1724         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1725         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1726       }
1727     }
1728     PetscCall(DMGetNumLabels(dm, &numLabels));
1729     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1730     for (l = 0; l < numLabels; ++l) {
1731       DMLabel     label;
1732       const char *name;
1733       PetscInt   *values;
1734       PetscInt    numValues, v;
1735 
1736       PetscCall(DMGetLabelName(dm, l, &name));
1737       PetscCall(DMGetLabel(dm, name, &label));
1738       PetscCall(DMLabelGetNumValues(label, &numValues));
1739       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1740 
1741       { // Extract array of DMLabel values so it can be sorted
1742         IS              is_values;
1743         const PetscInt *is_values_local = NULL;
1744 
1745         PetscCall(DMLabelGetValueIS(label, &is_values));
1746         PetscCall(ISGetIndices(is_values, &is_values_local));
1747         PetscCall(PetscMalloc1(numValues, &values));
1748         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1749         PetscCall(PetscSortInt(numValues, values));
1750         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1751         PetscCall(ISDestroy(&is_values));
1752       }
1753       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1754       for (v = 0; v < numValues; ++v) {
1755         PetscInt size;
1756 
1757         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1758         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1759         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1760       }
1761       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1762       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1763       PetscCall(PetscFree(values));
1764     }
1765     {
1766       char    **labelNames;
1767       PetscInt  Nl = numLabels;
1768       PetscBool flg;
1769 
1770       PetscCall(PetscMalloc1(Nl, &labelNames));
1771       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1772       for (l = 0; l < Nl; ++l) {
1773         DMLabel label;
1774 
1775         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1776         if (flg) {
1777           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1778           PetscCall(DMLabelView(label, viewer));
1779         }
1780         PetscCall(PetscFree(labelNames[l]));
1781       }
1782       PetscCall(PetscFree(labelNames));
1783     }
1784     /* If no fields are specified, people do not want to see adjacency */
1785     if (dm->Nf) {
1786       PetscInt f;
1787 
1788       for (f = 0; f < dm->Nf; ++f) {
1789         const char *name;
1790 
1791         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1792         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1793         PetscCall(PetscViewerASCIIPushTab(viewer));
1794         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1795         if (dm->fields[f].adjacency[0]) {
1796           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1797           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1798         } else {
1799           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1800           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1801         }
1802         PetscCall(PetscViewerASCIIPopTab(viewer));
1803       }
1804     }
1805     DMPlexTransform tr;
1806 
1807     PetscCall(DMPlexGetTransform(dm, &tr));
1808     if (tr) {
1809       PetscCall(PetscViewerASCIIPushTab(viewer));
1810       PetscCall(PetscViewerASCIIPrintf(viewer, "Created using transform:\n"));
1811       PetscCall(DMPlexTransformView(tr, viewer));
1812       PetscCall(PetscViewerASCIIPopTab(viewer));
1813     }
1814     PetscCall(DMGetCoarseDM(dm, &cdm));
1815     if (cdm) {
1816       PetscCall(PetscViewerASCIIPushTab(viewer));
1817       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1818       PetscCall(DMPlexView_Ascii(cdm, viewer));
1819       PetscCall(PetscViewerASCIIPopTab(viewer));
1820     }
1821   }
1822   PetscFunctionReturn(PETSC_SUCCESS);
1823 }
1824 
1825 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1826 {
1827   DMPolytopeType ct;
1828   PetscMPIInt    rank;
1829   PetscInt       cdim;
1830 
1831   PetscFunctionBegin;
1832   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1833   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1834   PetscCall(DMGetCoordinateDim(dm, &cdim));
1835   switch (ct) {
1836   case DM_POLYTOPE_SEGMENT:
1837   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1838     switch (cdim) {
1839     case 1: {
1840       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1841       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1842 
1843       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1844       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1845       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1846     } break;
1847     case 2: {
1848       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1849       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1850       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1851 
1852       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1853       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1854       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1855     } break;
1856     default:
1857       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1858     }
1859     break;
1860   case DM_POLYTOPE_TRIANGLE:
1861     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1862     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1863     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1864     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1865     break;
1866   case DM_POLYTOPE_QUADRILATERAL:
1867     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1868     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1869     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1870     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1871     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1872     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1873     break;
1874   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1875     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1876     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1877     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1878     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1879     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1880     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1881     break;
1882   case DM_POLYTOPE_FV_GHOST:
1883     break;
1884   default:
1885     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1886   }
1887   PetscFunctionReturn(PETSC_SUCCESS);
1888 }
1889 
1890 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1891 {
1892   PetscReal   centroid[2] = {0., 0.};
1893   PetscMPIInt rank;
1894   PetscMPIInt fillColor;
1895 
1896   PetscFunctionBegin;
1897   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1898   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1899   for (PetscInt v = 0; v < Nv; ++v) {
1900     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1901     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1902   }
1903   for (PetscInt e = 0; e < Nv; ++e) {
1904     refCoords[0] = refVertices[e * 2 + 0];
1905     refCoords[1] = refVertices[e * 2 + 1];
1906     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1907       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1908       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1909     }
1910     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1911     for (PetscInt d = 0; d < edgeDiv; ++d) {
1912       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));
1913       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1914     }
1915   }
1916   PetscFunctionReturn(PETSC_SUCCESS);
1917 }
1918 
1919 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1920 {
1921   DMPolytopeType ct;
1922 
1923   PetscFunctionBegin;
1924   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1925   switch (ct) {
1926   case DM_POLYTOPE_TRIANGLE: {
1927     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1928 
1929     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1930   } break;
1931   case DM_POLYTOPE_QUADRILATERAL: {
1932     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1933 
1934     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1935   } break;
1936   default:
1937     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1938   }
1939   PetscFunctionReturn(PETSC_SUCCESS);
1940 }
1941 
1942 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1943 {
1944   PetscDraw    draw;
1945   DM           cdm;
1946   PetscSection coordSection;
1947   Vec          coordinates;
1948   PetscReal    xyl[3], xyr[3];
1949   PetscReal   *refCoords, *edgeCoords;
1950   PetscBool    isnull, drawAffine;
1951   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1952 
1953   PetscFunctionBegin;
1954   PetscCall(DMGetCoordinateDim(dm, &dim));
1955   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1956   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1957   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1958   edgeDiv    = cDegree + 1;
1959   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1960   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1961   PetscCall(DMGetCoordinateDM(dm, &cdm));
1962   PetscCall(DMGetLocalSection(cdm, &coordSection));
1963   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1964   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1965   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1966 
1967   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1968   PetscCall(PetscDrawIsNull(draw, &isnull));
1969   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1970   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1971 
1972   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1973   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1974   PetscCall(PetscDrawClear(draw));
1975 
1976   for (c = cStart; c < cEnd; ++c) {
1977     PetscScalar       *coords = NULL;
1978     const PetscScalar *coords_arr;
1979     PetscInt           numCoords;
1980     PetscBool          isDG;
1981 
1982     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1983     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1984     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1985     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1986   }
1987   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1988   PetscCall(PetscDrawFlush(draw));
1989   PetscCall(PetscDrawPause(draw));
1990   PetscCall(PetscDrawSave(draw));
1991   PetscFunctionReturn(PETSC_SUCCESS);
1992 }
1993 
1994 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1995 {
1996   DM           odm = dm, rdm = dm, cdm;
1997   PetscFE      fe;
1998   PetscSpace   sp;
1999   PetscClassId id;
2000   PetscInt     degree;
2001   PetscBool    hoView = PETSC_TRUE;
2002 
2003   PetscFunctionBegin;
2004   PetscObjectOptionsBegin((PetscObject)dm);
2005   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2006   PetscOptionsEnd();
2007   PetscCall(PetscObjectReference((PetscObject)dm));
2008   *hdm = dm;
2009   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2010   PetscCall(DMGetCoordinateDM(dm, &cdm));
2011   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2012   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2013   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2014   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2015   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2016   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2017     DM  cdm, rcdm;
2018     Mat In;
2019     Vec cl, rcl;
2020 
2021     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2022     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
2023     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2024     PetscCall(DMGetCoordinateDM(odm, &cdm));
2025     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2026     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2027     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2028     PetscCall(DMSetCoarseDM(rcdm, cdm));
2029     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2030     PetscCall(MatMult(In, cl, rcl));
2031     PetscCall(MatDestroy(&In));
2032     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2033     PetscCall(DMDestroy(&odm));
2034     odm = rdm;
2035   }
2036   *hdm = rdm;
2037   PetscFunctionReturn(PETSC_SUCCESS);
2038 }
2039 
2040 #if defined(PETSC_HAVE_EXODUSII)
2041   #include <exodusII.h>
2042   #include <petscviewerexodusii.h>
2043 #endif
2044 
2045 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2046 {
2047   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2048   char      name[PETSC_MAX_PATH_LEN];
2049 
2050   PetscFunctionBegin;
2051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2052   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2053   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2054   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2055   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2056   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2057   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2060   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2061   if (iascii) {
2062     PetscViewerFormat format;
2063     PetscCall(PetscViewerGetFormat(viewer, &format));
2064     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2065     else PetscCall(DMPlexView_Ascii(dm, viewer));
2066   } else if (ishdf5) {
2067 #if defined(PETSC_HAVE_HDF5)
2068     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2069 #else
2070     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2071 #endif
2072   } else if (isvtk) {
2073     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2074   } else if (isdraw) {
2075     DM hdm;
2076 
2077     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2078     PetscCall(DMPlexView_Draw(hdm, viewer));
2079     PetscCall(DMDestroy(&hdm));
2080   } else if (isglvis) {
2081     PetscCall(DMPlexView_GLVis(dm, viewer));
2082 #if defined(PETSC_HAVE_EXODUSII)
2083   } else if (isexodus) {
2084     /*
2085       exodusII requires that all sets be part of exactly one cell set.
2086       If the dm does not have a "Cell Sets" label defined, we create one
2087       with ID 1, containing all cells.
2088       Note that if the Cell Sets label is defined but does not cover all cells,
2089       we may still have a problem. This should probably be checked here or in the viewer;
2090     */
2091     PetscInt numCS;
2092     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2093     if (!numCS) {
2094       PetscInt cStart, cEnd, c;
2095       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2096       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2097       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2098     }
2099     PetscCall(DMView_PlexExodusII(dm, viewer));
2100 #endif
2101 #if defined(PETSC_HAVE_CGNS)
2102   } else if (iscgns) {
2103     PetscCall(DMView_PlexCGNS(dm, viewer));
2104 #endif
2105   } else if (ispython) {
2106     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2107   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2108   /* Optionally view the partition */
2109   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2110   if (flg) {
2111     Vec ranks;
2112     PetscCall(DMPlexCreateRankField(dm, &ranks));
2113     PetscCall(VecView(ranks, viewer));
2114     PetscCall(VecDestroy(&ranks));
2115   }
2116   /* Optionally view a label */
2117   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2118   if (flg) {
2119     DMLabel label;
2120     Vec     val;
2121 
2122     PetscCall(DMGetLabel(dm, name, &label));
2123     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2124     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2125     PetscCall(VecView(val, viewer));
2126     PetscCall(VecDestroy(&val));
2127   }
2128   PetscFunctionReturn(PETSC_SUCCESS);
2129 }
2130 
2131 /*@
2132   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2133 
2134   Collective
2135 
2136   Input Parameters:
2137 + dm     - The `DM` whose topology is to be saved
2138 - viewer - The `PetscViewer` to save it in
2139 
2140   Level: advanced
2141 
2142 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2143 @*/
2144 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2145 {
2146   PetscBool ishdf5;
2147 
2148   PetscFunctionBegin;
2149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2150   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2151   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2152   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2153   if (ishdf5) {
2154 #if defined(PETSC_HAVE_HDF5)
2155     PetscViewerFormat format;
2156     PetscCall(PetscViewerGetFormat(viewer, &format));
2157     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2158       IS globalPointNumbering;
2159 
2160       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2161       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2162       PetscCall(ISDestroy(&globalPointNumbering));
2163     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2164 #else
2165     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2166 #endif
2167   }
2168   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2169   PetscFunctionReturn(PETSC_SUCCESS);
2170 }
2171 
2172 /*@
2173   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2174 
2175   Collective
2176 
2177   Input Parameters:
2178 + dm     - The `DM` whose coordinates are to be saved
2179 - viewer - The `PetscViewer` for saving
2180 
2181   Level: advanced
2182 
2183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2184 @*/
2185 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2186 {
2187   PetscBool ishdf5;
2188 
2189   PetscFunctionBegin;
2190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2191   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2192   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2193   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2194   if (ishdf5) {
2195 #if defined(PETSC_HAVE_HDF5)
2196     PetscViewerFormat format;
2197     PetscCall(PetscViewerGetFormat(viewer, &format));
2198     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2199       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2200     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2201 #else
2202     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2203 #endif
2204   }
2205   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2206   PetscFunctionReturn(PETSC_SUCCESS);
2207 }
2208 
2209 /*@
2210   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2211 
2212   Collective
2213 
2214   Input Parameters:
2215 + dm     - The `DM` whose labels are to be saved
2216 - viewer - The `PetscViewer` for saving
2217 
2218   Level: advanced
2219 
2220 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2221 @*/
2222 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2223 {
2224   PetscBool ishdf5;
2225 
2226   PetscFunctionBegin;
2227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2228   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2229   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2230   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2231   if (ishdf5) {
2232 #if defined(PETSC_HAVE_HDF5)
2233     IS                globalPointNumbering;
2234     PetscViewerFormat format;
2235 
2236     PetscCall(PetscViewerGetFormat(viewer, &format));
2237     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2238       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2239       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2240       PetscCall(ISDestroy(&globalPointNumbering));
2241     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2242 #else
2243     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2244 #endif
2245   }
2246   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2247   PetscFunctionReturn(PETSC_SUCCESS);
2248 }
2249 
2250 /*@
2251   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2252 
2253   Collective
2254 
2255   Input Parameters:
2256 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2257 . viewer    - The `PetscViewer` for saving
2258 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2259 
2260   Level: advanced
2261 
2262   Notes:
2263   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.
2264 
2265   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.
2266 
2267 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2268 @*/
2269 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2270 {
2271   PetscBool ishdf5;
2272 
2273   PetscFunctionBegin;
2274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2275   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2276   if (!sectiondm) sectiondm = dm;
2277   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2278   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2279   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2280   if (ishdf5) {
2281 #if defined(PETSC_HAVE_HDF5)
2282     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2283 #else
2284     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2285 #endif
2286   }
2287   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2288   PetscFunctionReturn(PETSC_SUCCESS);
2289 }
2290 
2291 /*@
2292   DMPlexGlobalVectorView - Saves a global vector
2293 
2294   Collective
2295 
2296   Input Parameters:
2297 + dm        - The `DM` that represents the topology
2298 . viewer    - The `PetscViewer` to save data with
2299 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2300 - vec       - The global vector to be saved
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   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.
2306 
2307   Calling sequence:
2308 .vb
2309        DMCreate(PETSC_COMM_WORLD, &dm);
2310        DMSetType(dm, DMPLEX);
2311        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2312        DMClone(dm, &sectiondm);
2313        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2314        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2315        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2316        PetscSectionSetChart(section, pStart, pEnd);
2317        PetscSectionSetUp(section);
2318        DMSetLocalSection(sectiondm, section);
2319        PetscSectionDestroy(&section);
2320        DMGetGlobalVector(sectiondm, &vec);
2321        PetscObjectSetName((PetscObject)vec, "vec_name");
2322        DMPlexTopologyView(dm, viewer);
2323        DMPlexSectionView(dm, viewer, sectiondm);
2324        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2325        DMRestoreGlobalVector(sectiondm, &vec);
2326        DMDestroy(&sectiondm);
2327        DMDestroy(&dm);
2328 .ve
2329 
2330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2331 @*/
2332 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2333 {
2334   PetscBool ishdf5;
2335 
2336   PetscFunctionBegin;
2337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2338   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2339   if (!sectiondm) sectiondm = dm;
2340   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2341   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2342   /* Check consistency */
2343   {
2344     PetscSection section;
2345     PetscBool    includesConstraints;
2346     PetscInt     m, m1;
2347 
2348     PetscCall(VecGetLocalSize(vec, &m1));
2349     PetscCall(DMGetGlobalSection(sectiondm, &section));
2350     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2351     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2352     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2353     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2354   }
2355   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2356   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2357   if (ishdf5) {
2358 #if defined(PETSC_HAVE_HDF5)
2359     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2360 #else
2361     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2362 #endif
2363   }
2364   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2365   PetscFunctionReturn(PETSC_SUCCESS);
2366 }
2367 
2368 /*@
2369   DMPlexLocalVectorView - Saves a local vector
2370 
2371   Collective
2372 
2373   Input Parameters:
2374 + dm        - The `DM` that represents the topology
2375 . viewer    - The `PetscViewer` to save data with
2376 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2377 - vec       - The local vector to be saved
2378 
2379   Level: advanced
2380 
2381   Note:
2382   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.
2383 
2384   Calling sequence:
2385 .vb
2386        DMCreate(PETSC_COMM_WORLD, &dm);
2387        DMSetType(dm, DMPLEX);
2388        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2389        DMClone(dm, &sectiondm);
2390        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2391        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2392        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2393        PetscSectionSetChart(section, pStart, pEnd);
2394        PetscSectionSetUp(section);
2395        DMSetLocalSection(sectiondm, section);
2396        DMGetLocalVector(sectiondm, &vec);
2397        PetscObjectSetName((PetscObject)vec, "vec_name");
2398        DMPlexTopologyView(dm, viewer);
2399        DMPlexSectionView(dm, viewer, sectiondm);
2400        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2401        DMRestoreLocalVector(sectiondm, &vec);
2402        DMDestroy(&sectiondm);
2403        DMDestroy(&dm);
2404 .ve
2405 
2406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2407 @*/
2408 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2409 {
2410   PetscBool ishdf5;
2411 
2412   PetscFunctionBegin;
2413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2414   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2415   if (!sectiondm) sectiondm = dm;
2416   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2417   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2418   /* Check consistency */
2419   {
2420     PetscSection section;
2421     PetscBool    includesConstraints;
2422     PetscInt     m, m1;
2423 
2424     PetscCall(VecGetLocalSize(vec, &m1));
2425     PetscCall(DMGetLocalSection(sectiondm, &section));
2426     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2427     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2428     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2429     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2430   }
2431   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2432   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2433   if (ishdf5) {
2434 #if defined(PETSC_HAVE_HDF5)
2435     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2436 #else
2437     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2438 #endif
2439   }
2440   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2441   PetscFunctionReturn(PETSC_SUCCESS);
2442 }
2443 
2444 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2445 {
2446   PetscBool ishdf5;
2447 
2448   PetscFunctionBegin;
2449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2450   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2452   if (ishdf5) {
2453 #if defined(PETSC_HAVE_HDF5)
2454     PetscViewerFormat format;
2455     PetscCall(PetscViewerGetFormat(viewer, &format));
2456     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2457       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2458     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2459       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2460     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2461     PetscFunctionReturn(PETSC_SUCCESS);
2462 #else
2463     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2464 #endif
2465   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2466 }
2467 
2468 /*@
2469   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2470 
2471   Collective
2472 
2473   Input Parameters:
2474 + dm     - The `DM` into which the topology is loaded
2475 - viewer - The `PetscViewer` for the saved topology
2476 
2477   Output Parameter:
2478 . 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;
2479   `NULL` if unneeded
2480 
2481   Level: advanced
2482 
2483 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2484           `PetscViewer`, `PetscSF`
2485 @*/
2486 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2487 {
2488   PetscBool ishdf5;
2489 
2490   PetscFunctionBegin;
2491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2492   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2493   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2494   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2495   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2496   if (ishdf5) {
2497 #if defined(PETSC_HAVE_HDF5)
2498     PetscViewerFormat format;
2499     PetscCall(PetscViewerGetFormat(viewer, &format));
2500     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2501       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2502     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2503 #else
2504     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2505 #endif
2506   }
2507   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2508   PetscFunctionReturn(PETSC_SUCCESS);
2509 }
2510 
2511 /*@
2512   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2513 
2514   Collective
2515 
2516   Input Parameters:
2517 + dm                   - The `DM` into which the coordinates are loaded
2518 . viewer               - The `PetscViewer` for the saved coordinates
2519 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2520 
2521   Level: advanced
2522 
2523 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2524           `PetscSF`, `PetscViewer`
2525 @*/
2526 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2527 {
2528   PetscBool ishdf5;
2529 
2530   PetscFunctionBegin;
2531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2532   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2533   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2535   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2536   if (ishdf5) {
2537 #if defined(PETSC_HAVE_HDF5)
2538     PetscViewerFormat format;
2539     PetscCall(PetscViewerGetFormat(viewer, &format));
2540     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2541       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2542     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2543 #else
2544     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2545 #endif
2546   }
2547   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2548   PetscFunctionReturn(PETSC_SUCCESS);
2549 }
2550 
2551 /*@
2552   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2553 
2554   Collective
2555 
2556   Input Parameters:
2557 + dm                   - The `DM` into which the labels are loaded
2558 . viewer               - The `PetscViewer` for the saved labels
2559 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2560 
2561   Level: advanced
2562 
2563   Note:
2564   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2565 
2566 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2567           `PetscSF`, `PetscViewer`
2568 @*/
2569 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2570 {
2571   PetscBool ishdf5;
2572 
2573   PetscFunctionBegin;
2574   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2575   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2576   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2578   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2579   if (ishdf5) {
2580 #if defined(PETSC_HAVE_HDF5)
2581     PetscViewerFormat format;
2582 
2583     PetscCall(PetscViewerGetFormat(viewer, &format));
2584     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2585       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2586     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
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       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5671       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5672       for (PetscInt p = pStart; p < pEnd; p++) {
5673         PetscCall(DMLabelGetValue(label, p, &ct));
5674         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5675       }
5676     }
5677     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5678     if (PetscDefined(USE_DEBUG)) {
5679       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5680       PetscCall(DMLabelGetValue(label, cell, &ct));
5681       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5682       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5683     }
5684   }
5685   PetscFunctionReturn(PETSC_SUCCESS);
5686 }
5687 
5688 /*@
5689   DMPlexSetCellType - Set the polytope type of a given cell
5690 
5691   Not Collective
5692 
5693   Input Parameters:
5694 + dm       - The `DMPLEX` object
5695 . cell     - The cell
5696 - celltype - The polytope type of the cell
5697 
5698   Level: advanced
5699 
5700   Note:
5701   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5702   is executed. This function will override the computed type. However, if automatic classification will not succeed
5703   and a user wants to manually specify all types, the classification must be disabled by calling
5704   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5705 
5706 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5707 @*/
5708 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5709 {
5710   DM_Plex *mesh = (DM_Plex *)dm->data;
5711   DMLabel  label;
5712   PetscInt pStart, pEnd;
5713 
5714   PetscFunctionBegin;
5715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5716   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5717   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5718   PetscCall(DMLabelSetValue(label, cell, celltype));
5719   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5720   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5721   PetscFunctionReturn(PETSC_SUCCESS);
5722 }
5723 
5724 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5725 {
5726   PetscSection section;
5727   PetscInt     maxHeight;
5728   const char  *prefix;
5729 
5730   PetscFunctionBegin;
5731   PetscCall(DMClone(dm, cdm));
5732   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5733   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5734   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5735   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5736   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5737   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5738   PetscCall(DMSetLocalSection(*cdm, section));
5739   PetscCall(PetscSectionDestroy(&section));
5740 
5741   PetscCall(DMSetNumFields(*cdm, 1));
5742   PetscCall(DMCreateDS(*cdm));
5743   (*cdm)->cloneOpts = PETSC_TRUE;
5744   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5745   PetscFunctionReturn(PETSC_SUCCESS);
5746 }
5747 
5748 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5749 {
5750   Vec coordsLocal, cellCoordsLocal;
5751   DM  coordsDM, cellCoordsDM;
5752 
5753   PetscFunctionBegin;
5754   *field = NULL;
5755   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5756   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5757   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5758   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5759   if (coordsLocal && coordsDM) {
5760     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5761     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5762   }
5763   PetscFunctionReturn(PETSC_SUCCESS);
5764 }
5765 
5766 /*@
5767   DMPlexGetConeSection - Return a section which describes the layout of cone data
5768 
5769   Not Collective
5770 
5771   Input Parameter:
5772 . dm - The `DMPLEX` object
5773 
5774   Output Parameter:
5775 . section - The `PetscSection` object
5776 
5777   Level: developer
5778 
5779 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5780 @*/
5781 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5782 {
5783   DM_Plex *mesh = (DM_Plex *)dm->data;
5784 
5785   PetscFunctionBegin;
5786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5787   if (section) *section = mesh->coneSection;
5788   PetscFunctionReturn(PETSC_SUCCESS);
5789 }
5790 
5791 /*@
5792   DMPlexGetSupportSection - Return a section which describes the layout of support data
5793 
5794   Not Collective
5795 
5796   Input Parameter:
5797 . dm - The `DMPLEX` object
5798 
5799   Output Parameter:
5800 . section - The `PetscSection` object
5801 
5802   Level: developer
5803 
5804 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5805 @*/
5806 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5807 {
5808   DM_Plex *mesh = (DM_Plex *)dm->data;
5809 
5810   PetscFunctionBegin;
5811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5812   if (section) *section = mesh->supportSection;
5813   PetscFunctionReturn(PETSC_SUCCESS);
5814 }
5815 
5816 /*@C
5817   DMPlexGetCones - Return cone data
5818 
5819   Not Collective
5820 
5821   Input Parameter:
5822 . dm - The `DMPLEX` object
5823 
5824   Output Parameter:
5825 . cones - The cone for each point
5826 
5827   Level: developer
5828 
5829 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5830 @*/
5831 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5832 {
5833   DM_Plex *mesh = (DM_Plex *)dm->data;
5834 
5835   PetscFunctionBegin;
5836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5837   if (cones) *cones = mesh->cones;
5838   PetscFunctionReturn(PETSC_SUCCESS);
5839 }
5840 
5841 /*@C
5842   DMPlexGetConeOrientations - Return cone orientation data
5843 
5844   Not Collective
5845 
5846   Input Parameter:
5847 . dm - The `DMPLEX` object
5848 
5849   Output Parameter:
5850 . coneOrientations - The array of cone orientations for all points
5851 
5852   Level: developer
5853 
5854   Notes:
5855   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5856   as returned by `DMPlexGetConeOrientation()`.
5857 
5858   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5859 
5860 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5861 @*/
5862 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5863 {
5864   DM_Plex *mesh = (DM_Plex *)dm->data;
5865 
5866   PetscFunctionBegin;
5867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5868   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5869   PetscFunctionReturn(PETSC_SUCCESS);
5870 }
5871 
5872 /* FEM Support */
5873 
5874 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5875 {
5876   PetscInt depth;
5877 
5878   PetscFunctionBegin;
5879   PetscCall(DMPlexGetDepth(plex, &depth));
5880   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5881   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5882   PetscFunctionReturn(PETSC_SUCCESS);
5883 }
5884 
5885 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5886 {
5887   PetscInt depth;
5888 
5889   PetscFunctionBegin;
5890   PetscCall(DMPlexGetDepth(plex, &depth));
5891   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5892   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5893   PetscFunctionReturn(PETSC_SUCCESS);
5894 }
5895 
5896 /*
5897  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5898  representing a line in the section.
5899 */
5900 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5901 {
5902   PetscObject  obj;
5903   PetscClassId id;
5904   PetscFE      fe = NULL;
5905 
5906   PetscFunctionBeginHot;
5907   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5908   PetscCall(DMGetField(dm, field, NULL, &obj));
5909   PetscCall(PetscObjectGetClassId(obj, &id));
5910   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5911 
5912   if (!fe) {
5913     /* Assume the full interpolated mesh is in the chart; lines in particular */
5914     /* An order k SEM disc has k-1 dofs on an edge */
5915     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5916     *k = *k / *Nc + 1;
5917   } else {
5918     PetscInt       dual_space_size, dim;
5919     PetscDualSpace dsp;
5920 
5921     PetscCall(DMGetDimension(dm, &dim));
5922     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5923     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5924     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5925     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5926     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5927   }
5928   PetscFunctionReturn(PETSC_SUCCESS);
5929 }
5930 
5931 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5932 {
5933   PetscFunctionBeginHot;
5934   if (tensor) {
5935     *dof = PetscPowInt(k + 1, dim);
5936   } else {
5937     switch (dim) {
5938     case 1:
5939       *dof = k + 1;
5940       break;
5941     case 2:
5942       *dof = ((k + 1) * (k + 2)) / 2;
5943       break;
5944     case 3:
5945       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5946       break;
5947     default:
5948       *dof = 0;
5949     }
5950   }
5951   PetscFunctionReturn(PETSC_SUCCESS);
5952 }
5953 
5954 /*@
5955   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5956   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5957   section provided (or the section of the `DM`).
5958 
5959   Input Parameters:
5960 + dm      - The `DM`
5961 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5962 - section - The `PetscSection` to reorder, or `NULL` for the default section
5963 
5964   Example:
5965   A typical interpolated single-quad mesh might order points as
5966 .vb
5967   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5968 
5969   v4 -- e6 -- v3
5970   |           |
5971   e7    c0    e8
5972   |           |
5973   v1 -- e5 -- v2
5974 .ve
5975 
5976   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5977   dofs in the order of points, e.g.,
5978 .vb
5979     c0 -> [0,1,2,3]
5980     v1 -> [4]
5981     ...
5982     e5 -> [8, 9]
5983 .ve
5984 
5985   which corresponds to the dofs
5986 .vb
5987     6   10  11  7
5988     13  2   3   15
5989     12  0   1   14
5990     4   8   9   5
5991 .ve
5992 
5993   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5994 .vb
5995   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5996 .ve
5997 
5998   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5999 .vb
6000    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6001 .ve
6002 
6003   Level: developer
6004 
6005   Notes:
6006   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6007   degree of the basis.
6008 
6009   This is required to run with libCEED.
6010 
6011 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6012 @*/
6013 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6014 {
6015   DMLabel   label;
6016   PetscInt  dim, depth = -1, eStart = -1, Nf;
6017   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6018 
6019   PetscFunctionBegin;
6020   PetscCall(DMGetDimension(dm, &dim));
6021   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6022   if (point < 0) {
6023     PetscInt sStart, sEnd;
6024 
6025     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6026     point = sEnd - sStart ? sStart : point;
6027   }
6028   PetscCall(DMPlexGetDepthLabel(dm, &label));
6029   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6030   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6031   if (depth == 1) {
6032     eStart = point;
6033   } else if (depth == dim) {
6034     const PetscInt *cone;
6035 
6036     PetscCall(DMPlexGetCone(dm, point, &cone));
6037     if (dim == 2) eStart = cone[0];
6038     else if (dim == 3) {
6039       const PetscInt *cone2;
6040       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6041       eStart = cone2[0];
6042     } 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);
6043   } 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);
6044 
6045   PetscCall(PetscSectionGetNumFields(section, &Nf));
6046   for (PetscInt d = 1; d <= dim; d++) {
6047     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6048     PetscInt *perm;
6049 
6050     for (f = 0; f < Nf; ++f) {
6051       PetscInt dof;
6052 
6053       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6054       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6055       if (!continuous && d < dim) continue;
6056       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6057       size += dof * Nc;
6058     }
6059     PetscCall(PetscMalloc1(size, &perm));
6060     for (f = 0; f < Nf; ++f) {
6061       switch (d) {
6062       case 1:
6063         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6064         if (!continuous && d < dim) continue;
6065         /*
6066          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6067          We want              [ vtx0; edge of length k-1; vtx1 ]
6068          */
6069         if (continuous) {
6070           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6071           for (i = 0; i < k - 1; i++)
6072             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6073           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6074           foffset = offset;
6075         } else {
6076           PetscInt dof;
6077 
6078           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6079           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6080           foffset = offset;
6081         }
6082         break;
6083       case 2:
6084         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6085         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6086         if (!continuous && d < dim) continue;
6087         /* The SEM order is
6088 
6089          v_lb, {e_b}, v_rb,
6090          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6091          v_lt, reverse {e_t}, v_rt
6092          */
6093         if (continuous) {
6094           const PetscInt of   = 0;
6095           const PetscInt oeb  = of + PetscSqr(k - 1);
6096           const PetscInt oer  = oeb + (k - 1);
6097           const PetscInt oet  = oer + (k - 1);
6098           const PetscInt oel  = oet + (k - 1);
6099           const PetscInt ovlb = oel + (k - 1);
6100           const PetscInt ovrb = ovlb + 1;
6101           const PetscInt ovrt = ovrb + 1;
6102           const PetscInt ovlt = ovrt + 1;
6103           PetscInt       o;
6104 
6105           /* bottom */
6106           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6107           for (o = oeb; o < oer; ++o)
6108             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6109           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6110           /* middle */
6111           for (i = 0; i < k - 1; ++i) {
6112             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6113             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6114               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6115             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6116           }
6117           /* top */
6118           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6119           for (o = oel - 1; o >= oet; --o)
6120             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6121           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6122           foffset = offset;
6123         } else {
6124           PetscInt dof;
6125 
6126           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6127           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6128           foffset = offset;
6129         }
6130         break;
6131       case 3:
6132         /* The original hex closure is
6133 
6134          {c,
6135          f_b, f_t, f_f, f_b, f_r, f_l,
6136          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6137          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6138          */
6139         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6140         if (!continuous && d < dim) continue;
6141         /* The SEM order is
6142          Bottom Slice
6143          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6144          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6145          v_blb, {e_bb}, v_brb,
6146 
6147          Middle Slice (j)
6148          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6149          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6150          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6151 
6152          Top Slice
6153          v_tlf, {e_tf}, v_trf,
6154          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6155          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6156          */
6157         if (continuous) {
6158           const PetscInt oc    = 0;
6159           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6160           const PetscInt oft   = ofb + PetscSqr(k - 1);
6161           const PetscInt off   = oft + PetscSqr(k - 1);
6162           const PetscInt ofk   = off + PetscSqr(k - 1);
6163           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6164           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6165           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6166           const PetscInt oebb  = oebl + (k - 1);
6167           const PetscInt oebr  = oebb + (k - 1);
6168           const PetscInt oebf  = oebr + (k - 1);
6169           const PetscInt oetf  = oebf + (k - 1);
6170           const PetscInt oetr  = oetf + (k - 1);
6171           const PetscInt oetb  = oetr + (k - 1);
6172           const PetscInt oetl  = oetb + (k - 1);
6173           const PetscInt oerf  = oetl + (k - 1);
6174           const PetscInt oelf  = oerf + (k - 1);
6175           const PetscInt oelb  = oelf + (k - 1);
6176           const PetscInt oerb  = oelb + (k - 1);
6177           const PetscInt ovblf = oerb + (k - 1);
6178           const PetscInt ovblb = ovblf + 1;
6179           const PetscInt ovbrb = ovblb + 1;
6180           const PetscInt ovbrf = ovbrb + 1;
6181           const PetscInt ovtlf = ovbrf + 1;
6182           const PetscInt ovtrf = ovtlf + 1;
6183           const PetscInt ovtrb = ovtrf + 1;
6184           const PetscInt ovtlb = ovtrb + 1;
6185           PetscInt       o, n;
6186 
6187           /* Bottom Slice */
6188           /*   bottom */
6189           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6190           for (o = oetf - 1; o >= oebf; --o)
6191             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6192           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6193           /*   middle */
6194           for (i = 0; i < k - 1; ++i) {
6195             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6196             for (n = 0; n < k - 1; ++n) {
6197               o = ofb + n * (k - 1) + i;
6198               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6199             }
6200             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6201           }
6202           /*   top */
6203           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6204           for (o = oebb; o < oebr; ++o)
6205             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6206           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6207 
6208           /* Middle Slice */
6209           for (j = 0; j < k - 1; ++j) {
6210             /*   bottom */
6211             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6212             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6213               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6214             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6215             /*   middle */
6216             for (i = 0; i < k - 1; ++i) {
6217               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6218               for (n = 0; n < k - 1; ++n)
6219                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6220               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6221             }
6222             /*   top */
6223             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6224             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6225               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6226             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6227           }
6228 
6229           /* Top Slice */
6230           /*   bottom */
6231           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6232           for (o = oetf; o < oetr; ++o)
6233             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6234           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6235           /*   middle */
6236           for (i = 0; i < k - 1; ++i) {
6237             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6238             for (n = 0; n < k - 1; ++n)
6239               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6240             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6241           }
6242           /*   top */
6243           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6244           for (o = oetl - 1; o >= oetb; --o)
6245             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6246           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6247 
6248           foffset = offset;
6249         } else {
6250           PetscInt dof;
6251 
6252           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6253           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6254           foffset = offset;
6255         }
6256         break;
6257       default:
6258         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6259       }
6260     }
6261     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6262     /* Check permutation */
6263     {
6264       PetscInt *check;
6265 
6266       PetscCall(PetscMalloc1(size, &check));
6267       for (i = 0; i < size; ++i) {
6268         check[i] = -1;
6269         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6270       }
6271       for (i = 0; i < size; ++i) check[perm[i]] = i;
6272       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6273       PetscCall(PetscFree(check));
6274     }
6275     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6276     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6277       PetscInt *loc_perm;
6278       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6279       for (PetscInt i = 0; i < size; i++) {
6280         loc_perm[i]        = perm[i];
6281         loc_perm[size + i] = size + perm[i];
6282       }
6283       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6284     }
6285   }
6286   PetscFunctionReturn(PETSC_SUCCESS);
6287 }
6288 
6289 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6290 {
6291   PetscDS  prob;
6292   PetscInt depth, Nf, h;
6293   DMLabel  label;
6294 
6295   PetscFunctionBeginHot;
6296   PetscCall(DMGetDS(dm, &prob));
6297   Nf      = prob->Nf;
6298   label   = dm->depthLabel;
6299   *dspace = NULL;
6300   if (field < Nf) {
6301     PetscObject disc = prob->disc[field];
6302 
6303     if (disc->classid == PETSCFE_CLASSID) {
6304       PetscDualSpace dsp;
6305 
6306       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6307       PetscCall(DMLabelGetNumValues(label, &depth));
6308       PetscCall(DMLabelGetValue(label, point, &h));
6309       h = depth - 1 - h;
6310       if (h) {
6311         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6312       } else {
6313         *dspace = dsp;
6314       }
6315     }
6316   }
6317   PetscFunctionReturn(PETSC_SUCCESS);
6318 }
6319 
6320 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6321 {
6322   PetscScalar       *array;
6323   const PetscScalar *vArray;
6324   const PetscInt    *cone, *coneO;
6325   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6326 
6327   PetscFunctionBeginHot;
6328   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6329   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6330   PetscCall(DMPlexGetCone(dm, point, &cone));
6331   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6332   if (!values || !*values) {
6333     if ((point >= pStart) && (point < pEnd)) {
6334       PetscInt dof;
6335 
6336       PetscCall(PetscSectionGetDof(section, point, &dof));
6337       size += dof;
6338     }
6339     for (p = 0; p < numPoints; ++p) {
6340       const PetscInt cp = cone[p];
6341       PetscInt       dof;
6342 
6343       if ((cp < pStart) || (cp >= pEnd)) continue;
6344       PetscCall(PetscSectionGetDof(section, cp, &dof));
6345       size += dof;
6346     }
6347     if (!values) {
6348       if (csize) *csize = size;
6349       PetscFunctionReturn(PETSC_SUCCESS);
6350     }
6351     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6352   } else {
6353     array = *values;
6354   }
6355   size = 0;
6356   PetscCall(VecGetArrayRead(v, &vArray));
6357   if ((point >= pStart) && (point < pEnd)) {
6358     PetscInt           dof, off, d;
6359     const PetscScalar *varr;
6360 
6361     PetscCall(PetscSectionGetDof(section, point, &dof));
6362     PetscCall(PetscSectionGetOffset(section, point, &off));
6363     varr = PetscSafePointerPlusOffset(vArray, off);
6364     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6365     size += dof;
6366   }
6367   for (p = 0; p < numPoints; ++p) {
6368     const PetscInt     cp = cone[p];
6369     PetscInt           o  = coneO[p];
6370     PetscInt           dof, off, d;
6371     const PetscScalar *varr;
6372 
6373     if ((cp < pStart) || (cp >= pEnd)) continue;
6374     PetscCall(PetscSectionGetDof(section, cp, &dof));
6375     PetscCall(PetscSectionGetOffset(section, cp, &off));
6376     varr = PetscSafePointerPlusOffset(vArray, off);
6377     if (o >= 0) {
6378       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6379     } else {
6380       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6381     }
6382     size += dof;
6383   }
6384   PetscCall(VecRestoreArrayRead(v, &vArray));
6385   if (!*values) {
6386     if (csize) *csize = size;
6387     *values = array;
6388   } else {
6389     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6390     *csize = size;
6391   }
6392   PetscFunctionReturn(PETSC_SUCCESS);
6393 }
6394 
6395 /* Compress out points not in the section */
6396 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6397 {
6398   const PetscInt np = *numPoints;
6399   PetscInt       pStart, pEnd, p, q;
6400 
6401   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6402   for (p = 0, q = 0; p < np; ++p) {
6403     const PetscInt r = points[p * 2];
6404     if ((r >= pStart) && (r < pEnd)) {
6405       points[q * 2]     = r;
6406       points[q * 2 + 1] = points[p * 2 + 1];
6407       ++q;
6408     }
6409   }
6410   *numPoints = q;
6411   return PETSC_SUCCESS;
6412 }
6413 
6414 /* Compressed closure does not apply closure permutation */
6415 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6416 {
6417   const PetscInt *cla = NULL;
6418   PetscInt        np, *pts = NULL;
6419 
6420   PetscFunctionBeginHot;
6421   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6422   if (!ornt && *clPoints) {
6423     PetscInt dof, off;
6424 
6425     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6426     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6427     PetscCall(ISGetIndices(*clPoints, &cla));
6428     np  = dof / 2;
6429     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6430   } else {
6431     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6432     PetscCall(CompressPoints_Private(section, &np, pts));
6433   }
6434   *numPoints = np;
6435   *points    = pts;
6436   *clp       = cla;
6437   PetscFunctionReturn(PETSC_SUCCESS);
6438 }
6439 
6440 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6441 {
6442   PetscFunctionBeginHot;
6443   if (!*clPoints) {
6444     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6445   } else {
6446     PetscCall(ISRestoreIndices(*clPoints, clp));
6447   }
6448   *numPoints = 0;
6449   *points    = NULL;
6450   *clSec     = NULL;
6451   *clPoints  = NULL;
6452   *clp       = NULL;
6453   PetscFunctionReturn(PETSC_SUCCESS);
6454 }
6455 
6456 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6457 {
6458   PetscInt            offset = 0, p;
6459   const PetscInt    **perms  = NULL;
6460   const PetscScalar **flips  = NULL;
6461 
6462   PetscFunctionBeginHot;
6463   *size = 0;
6464   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6465   for (p = 0; p < numPoints; p++) {
6466     const PetscInt     point = points[2 * p];
6467     const PetscInt    *perm  = perms ? perms[p] : NULL;
6468     const PetscScalar *flip  = flips ? flips[p] : NULL;
6469     PetscInt           dof, off, d;
6470     const PetscScalar *varr;
6471 
6472     PetscCall(PetscSectionGetDof(section, point, &dof));
6473     PetscCall(PetscSectionGetOffset(section, point, &off));
6474     varr = PetscSafePointerPlusOffset(vArray, off);
6475     if (clperm) {
6476       if (perm) {
6477         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6478       } else {
6479         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6480       }
6481       if (flip) {
6482         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6483       }
6484     } else {
6485       if (perm) {
6486         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6487       } else {
6488         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6489       }
6490       if (flip) {
6491         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6492       }
6493     }
6494     offset += dof;
6495   }
6496   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6497   *size = offset;
6498   PetscFunctionReturn(PETSC_SUCCESS);
6499 }
6500 
6501 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[])
6502 {
6503   PetscInt offset = 0, f;
6504 
6505   PetscFunctionBeginHot;
6506   *size = 0;
6507   for (f = 0; f < numFields; ++f) {
6508     PetscInt            p;
6509     const PetscInt    **perms = NULL;
6510     const PetscScalar **flips = NULL;
6511 
6512     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6513     for (p = 0; p < numPoints; p++) {
6514       const PetscInt     point = points[2 * p];
6515       PetscInt           fdof, foff, b;
6516       const PetscScalar *varr;
6517       const PetscInt    *perm = perms ? perms[p] : NULL;
6518       const PetscScalar *flip = flips ? flips[p] : NULL;
6519 
6520       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6521       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6522       varr = &vArray[foff];
6523       if (clperm) {
6524         if (perm) {
6525           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6526         } else {
6527           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6528         }
6529         if (flip) {
6530           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6531         }
6532       } else {
6533         if (perm) {
6534           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6535         } else {
6536           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6537         }
6538         if (flip) {
6539           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6540         }
6541       }
6542       offset += fdof;
6543     }
6544     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6545   }
6546   *size = offset;
6547   PetscFunctionReturn(PETSC_SUCCESS);
6548 }
6549 
6550 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6551 {
6552   PetscSection    clSection;
6553   IS              clPoints;
6554   PetscInt       *points = NULL;
6555   const PetscInt *clp, *perm = NULL;
6556   PetscInt        depth, numFields, numPoints, asize;
6557 
6558   PetscFunctionBeginHot;
6559   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6560   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6561   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6562   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6563   PetscCall(DMPlexGetDepth(dm, &depth));
6564   PetscCall(PetscSectionGetNumFields(section, &numFields));
6565   if (depth == 1 && numFields < 2) {
6566     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6567     PetscFunctionReturn(PETSC_SUCCESS);
6568   }
6569   /* Get points */
6570   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6571   /* Get sizes */
6572   asize = 0;
6573   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6574     PetscInt dof;
6575     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6576     asize += dof;
6577   }
6578   if (values) {
6579     const PetscScalar *vArray;
6580     PetscInt           size;
6581 
6582     if (*values) {
6583       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);
6584     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6585     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6586     PetscCall(VecGetArrayRead(v, &vArray));
6587     /* Get values */
6588     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6589     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6590     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6591     /* Cleanup array */
6592     PetscCall(VecRestoreArrayRead(v, &vArray));
6593   }
6594   if (csize) *csize = asize;
6595   /* Cleanup points */
6596   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6597   PetscFunctionReturn(PETSC_SUCCESS);
6598 }
6599 
6600 /*@C
6601   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6602 
6603   Not collective
6604 
6605   Input Parameters:
6606 + dm      - The `DM`
6607 . section - The section describing the layout in `v`, or `NULL` to use the default section
6608 . v       - The local vector
6609 - point   - The point in the `DM`
6610 
6611   Input/Output Parameters:
6612 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6613 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6614            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6615 
6616   Level: intermediate
6617 
6618   Notes:
6619   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6620   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6621   assembly function, and a user may already have allocated storage for this operation.
6622 
6623   A typical use could be
6624 .vb
6625    values = NULL;
6626    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6627    for (cl = 0; cl < clSize; ++cl) {
6628      <Compute on closure>
6629    }
6630    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6631 .ve
6632   or
6633 .vb
6634    PetscMalloc1(clMaxSize, &values);
6635    for (p = pStart; p < pEnd; ++p) {
6636      clSize = clMaxSize;
6637      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6638      for (cl = 0; cl < clSize; ++cl) {
6639        <Compute on closure>
6640      }
6641    }
6642    PetscFree(values);
6643 .ve
6644 
6645   Fortran Notes:
6646   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6647   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6648 
6649   `values` must be declared with
6650 .vb
6651   PetscScalar,dimension(:),pointer   :: values
6652 .ve
6653   and it will be allocated internally by PETSc to hold the values returned
6654 
6655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6656 @*/
6657 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6658 {
6659   PetscFunctionBeginHot;
6660   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6661   PetscFunctionReturn(PETSC_SUCCESS);
6662 }
6663 
6664 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6665 {
6666   DMLabel            depthLabel;
6667   PetscSection       clSection;
6668   IS                 clPoints;
6669   PetscScalar       *array;
6670   const PetscScalar *vArray;
6671   PetscInt          *points = NULL;
6672   const PetscInt    *clp, *perm = NULL;
6673   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6674 
6675   PetscFunctionBeginHot;
6676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6677   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6678   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6679   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6680   PetscCall(DMPlexGetDepth(dm, &mdepth));
6681   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6682   PetscCall(PetscSectionGetNumFields(section, &numFields));
6683   if (mdepth == 1 && numFields < 2) {
6684     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6685     PetscFunctionReturn(PETSC_SUCCESS);
6686   }
6687   /* Get points */
6688   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6689   for (clsize = 0, p = 0; p < Np; p++) {
6690     PetscInt dof;
6691     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6692     clsize += dof;
6693   }
6694   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6695   /* Filter points */
6696   for (p = 0; p < numPoints * 2; p += 2) {
6697     PetscInt dep;
6698 
6699     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6700     if (dep != depth) continue;
6701     points[Np * 2 + 0] = points[p];
6702     points[Np * 2 + 1] = points[p + 1];
6703     ++Np;
6704   }
6705   /* Get array */
6706   if (!values || !*values) {
6707     PetscInt asize = 0, dof;
6708 
6709     for (p = 0; p < Np * 2; p += 2) {
6710       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6711       asize += dof;
6712     }
6713     if (!values) {
6714       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6715       if (csize) *csize = asize;
6716       PetscFunctionReturn(PETSC_SUCCESS);
6717     }
6718     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6719   } else {
6720     array = *values;
6721   }
6722   PetscCall(VecGetArrayRead(v, &vArray));
6723   /* Get values */
6724   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6725   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6726   /* Cleanup points */
6727   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6728   /* Cleanup array */
6729   PetscCall(VecRestoreArrayRead(v, &vArray));
6730   if (!*values) {
6731     if (csize) *csize = size;
6732     *values = array;
6733   } else {
6734     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6735     *csize = size;
6736   }
6737   PetscFunctionReturn(PETSC_SUCCESS);
6738 }
6739 
6740 /*@C
6741   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6742 
6743   Not collective
6744 
6745   Input Parameters:
6746 + dm      - The `DM`
6747 . section - The section describing the layout in `v`, or `NULL` to use the default section
6748 . v       - The local vector
6749 . point   - The point in the `DM`
6750 . csize   - The number of values in the closure, or `NULL`
6751 - values  - The array of values
6752 
6753   Level: intermediate
6754 
6755   Note:
6756   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6757 
6758   Fortran Note:
6759   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6760   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6761 
6762 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6763 @*/
6764 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6765 {
6766   PetscInt size = 0;
6767 
6768   PetscFunctionBegin;
6769   /* Should work without recalculating size */
6770   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6771   *values = NULL;
6772   PetscFunctionReturn(PETSC_SUCCESS);
6773 }
6774 
6775 static inline void add(PetscScalar *x, PetscScalar y)
6776 {
6777   *x += y;
6778 }
6779 static inline void insert(PetscScalar *x, PetscScalar y)
6780 {
6781   *x = y;
6782 }
6783 
6784 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[])
6785 {
6786   PetscInt        cdof;  /* The number of constraints on this point */
6787   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6788   PetscScalar    *a;
6789   PetscInt        off, cind = 0, k;
6790 
6791   PetscFunctionBegin;
6792   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6793   PetscCall(PetscSectionGetOffset(section, point, &off));
6794   a = &array[off];
6795   if (!cdof || setBC) {
6796     if (clperm) {
6797       if (perm) {
6798         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6799       } else {
6800         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6801       }
6802     } else {
6803       if (perm) {
6804         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6805       } else {
6806         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6807       }
6808     }
6809   } else {
6810     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6811     if (clperm) {
6812       if (perm) {
6813         for (k = 0; k < dof; ++k) {
6814           if ((cind < cdof) && (k == cdofs[cind])) {
6815             ++cind;
6816             continue;
6817           }
6818           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6819         }
6820       } else {
6821         for (k = 0; k < dof; ++k) {
6822           if ((cind < cdof) && (k == cdofs[cind])) {
6823             ++cind;
6824             continue;
6825           }
6826           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6827         }
6828       }
6829     } else {
6830       if (perm) {
6831         for (k = 0; k < dof; ++k) {
6832           if ((cind < cdof) && (k == cdofs[cind])) {
6833             ++cind;
6834             continue;
6835           }
6836           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6837         }
6838       } else {
6839         for (k = 0; k < dof; ++k) {
6840           if ((cind < cdof) && (k == cdofs[cind])) {
6841             ++cind;
6842             continue;
6843           }
6844           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6845         }
6846       }
6847     }
6848   }
6849   PetscFunctionReturn(PETSC_SUCCESS);
6850 }
6851 
6852 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[])
6853 {
6854   PetscInt        cdof;  /* The number of constraints on this point */
6855   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6856   PetscScalar    *a;
6857   PetscInt        off, cind = 0, k;
6858 
6859   PetscFunctionBegin;
6860   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6861   PetscCall(PetscSectionGetOffset(section, point, &off));
6862   a = &array[off];
6863   if (cdof) {
6864     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6865     if (clperm) {
6866       if (perm) {
6867         for (k = 0; k < dof; ++k) {
6868           if ((cind < cdof) && (k == cdofs[cind])) {
6869             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6870             cind++;
6871           }
6872         }
6873       } else {
6874         for (k = 0; k < dof; ++k) {
6875           if ((cind < cdof) && (k == cdofs[cind])) {
6876             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6877             cind++;
6878           }
6879         }
6880       }
6881     } else {
6882       if (perm) {
6883         for (k = 0; k < dof; ++k) {
6884           if ((cind < cdof) && (k == cdofs[cind])) {
6885             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6886             cind++;
6887           }
6888         }
6889       } else {
6890         for (k = 0; k < dof; ++k) {
6891           if ((cind < cdof) && (k == cdofs[cind])) {
6892             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6893             cind++;
6894           }
6895         }
6896       }
6897     }
6898   }
6899   PetscFunctionReturn(PETSC_SUCCESS);
6900 }
6901 
6902 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[])
6903 {
6904   PetscScalar    *a;
6905   PetscInt        fdof, foff, fcdof, foffset = *offset;
6906   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6907   PetscInt        cind = 0, b;
6908 
6909   PetscFunctionBegin;
6910   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6911   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6912   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6913   a = &array[foff];
6914   if (!fcdof || setBC) {
6915     if (clperm) {
6916       if (perm) {
6917         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6918       } else {
6919         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6920       }
6921     } else {
6922       if (perm) {
6923         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6924       } else {
6925         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6926       }
6927     }
6928   } else {
6929     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6930     if (clperm) {
6931       if (perm) {
6932         for (b = 0; b < fdof; b++) {
6933           if ((cind < fcdof) && (b == fcdofs[cind])) {
6934             ++cind;
6935             continue;
6936           }
6937           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6938         }
6939       } else {
6940         for (b = 0; b < fdof; b++) {
6941           if ((cind < fcdof) && (b == fcdofs[cind])) {
6942             ++cind;
6943             continue;
6944           }
6945           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6946         }
6947       }
6948     } else {
6949       if (perm) {
6950         for (b = 0; b < fdof; b++) {
6951           if ((cind < fcdof) && (b == fcdofs[cind])) {
6952             ++cind;
6953             continue;
6954           }
6955           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6956         }
6957       } else {
6958         for (b = 0; b < fdof; b++) {
6959           if ((cind < fcdof) && (b == fcdofs[cind])) {
6960             ++cind;
6961             continue;
6962           }
6963           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6964         }
6965       }
6966     }
6967   }
6968   *offset += fdof;
6969   PetscFunctionReturn(PETSC_SUCCESS);
6970 }
6971 
6972 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[])
6973 {
6974   PetscScalar    *a;
6975   PetscInt        fdof, foff, fcdof, foffset = *offset;
6976   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6977   PetscInt        Nc, cind = 0, ncind = 0, b;
6978   PetscBool       ncSet, fcSet;
6979 
6980   PetscFunctionBegin;
6981   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6982   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6983   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6984   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6985   a = &array[foff];
6986   if (fcdof) {
6987     /* We just override fcdof and fcdofs with Ncc and comps */
6988     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6989     if (clperm) {
6990       if (perm) {
6991         if (comps) {
6992           for (b = 0; b < fdof; b++) {
6993             ncSet = fcSet = PETSC_FALSE;
6994             if (b % Nc == comps[ncind]) {
6995               ncind = (ncind + 1) % Ncc;
6996               ncSet = PETSC_TRUE;
6997             }
6998             if ((cind < fcdof) && (b == fcdofs[cind])) {
6999               ++cind;
7000               fcSet = PETSC_TRUE;
7001             }
7002             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7003           }
7004         } else {
7005           for (b = 0; b < fdof; b++) {
7006             if ((cind < fcdof) && (b == fcdofs[cind])) {
7007               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7008               ++cind;
7009             }
7010           }
7011         }
7012       } else {
7013         if (comps) {
7014           for (b = 0; b < fdof; b++) {
7015             ncSet = fcSet = PETSC_FALSE;
7016             if (b % Nc == comps[ncind]) {
7017               ncind = (ncind + 1) % Ncc;
7018               ncSet = PETSC_TRUE;
7019             }
7020             if ((cind < fcdof) && (b == fcdofs[cind])) {
7021               ++cind;
7022               fcSet = PETSC_TRUE;
7023             }
7024             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7025           }
7026         } else {
7027           for (b = 0; b < fdof; b++) {
7028             if ((cind < fcdof) && (b == fcdofs[cind])) {
7029               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7030               ++cind;
7031             }
7032           }
7033         }
7034       }
7035     } else {
7036       if (perm) {
7037         if (comps) {
7038           for (b = 0; b < fdof; b++) {
7039             ncSet = fcSet = PETSC_FALSE;
7040             if (b % Nc == comps[ncind]) {
7041               ncind = (ncind + 1) % Ncc;
7042               ncSet = PETSC_TRUE;
7043             }
7044             if ((cind < fcdof) && (b == fcdofs[cind])) {
7045               ++cind;
7046               fcSet = PETSC_TRUE;
7047             }
7048             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7049           }
7050         } else {
7051           for (b = 0; b < fdof; b++) {
7052             if ((cind < fcdof) && (b == fcdofs[cind])) {
7053               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7054               ++cind;
7055             }
7056           }
7057         }
7058       } else {
7059         if (comps) {
7060           for (b = 0; b < fdof; b++) {
7061             ncSet = fcSet = PETSC_FALSE;
7062             if (b % Nc == comps[ncind]) {
7063               ncind = (ncind + 1) % Ncc;
7064               ncSet = PETSC_TRUE;
7065             }
7066             if ((cind < fcdof) && (b == fcdofs[cind])) {
7067               ++cind;
7068               fcSet = PETSC_TRUE;
7069             }
7070             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7071           }
7072         } else {
7073           for (b = 0; b < fdof; b++) {
7074             if ((cind < fcdof) && (b == fcdofs[cind])) {
7075               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7076               ++cind;
7077             }
7078           }
7079         }
7080       }
7081     }
7082   }
7083   *offset += fdof;
7084   PetscFunctionReturn(PETSC_SUCCESS);
7085 }
7086 
7087 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7088 {
7089   PetscScalar    *array;
7090   const PetscInt *cone, *coneO;
7091   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7092 
7093   PetscFunctionBeginHot;
7094   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7095   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7096   PetscCall(DMPlexGetCone(dm, point, &cone));
7097   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7098   PetscCall(VecGetArray(v, &array));
7099   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7100     const PetscInt cp = !p ? point : cone[p - 1];
7101     const PetscInt o  = !p ? 0 : coneO[p - 1];
7102 
7103     if ((cp < pStart) || (cp >= pEnd)) {
7104       dof = 0;
7105       continue;
7106     }
7107     PetscCall(PetscSectionGetDof(section, cp, &dof));
7108     /* ADD_VALUES */
7109     {
7110       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7111       PetscScalar    *a;
7112       PetscInt        cdof, coff, cind = 0, k;
7113 
7114       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7115       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7116       a = &array[coff];
7117       if (!cdof) {
7118         if (o >= 0) {
7119           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7120         } else {
7121           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7122         }
7123       } else {
7124         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7125         if (o >= 0) {
7126           for (k = 0; k < dof; ++k) {
7127             if ((cind < cdof) && (k == cdofs[cind])) {
7128               ++cind;
7129               continue;
7130             }
7131             a[k] += values[off + k];
7132           }
7133         } else {
7134           for (k = 0; k < dof; ++k) {
7135             if ((cind < cdof) && (k == cdofs[cind])) {
7136               ++cind;
7137               continue;
7138             }
7139             a[k] += values[off + dof - k - 1];
7140           }
7141         }
7142       }
7143     }
7144   }
7145   PetscCall(VecRestoreArray(v, &array));
7146   PetscFunctionReturn(PETSC_SUCCESS);
7147 }
7148 
7149 /*@C
7150   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7151 
7152   Not collective
7153 
7154   Input Parameters:
7155 + dm      - The `DM`
7156 . section - The section describing the layout in `v`, or `NULL` to use the default section
7157 . v       - The local vector
7158 . point   - The point in the `DM`
7159 . values  - The array of values
7160 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7161             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7162 
7163   Level: intermediate
7164 
7165   Note:
7166   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7167 
7168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7169 @*/
7170 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7171 {
7172   PetscSection    clSection;
7173   IS              clPoints;
7174   PetscScalar    *array;
7175   PetscInt       *points = NULL;
7176   const PetscInt *clp, *clperm = NULL;
7177   PetscInt        depth, numFields, numPoints, p, clsize;
7178 
7179   PetscFunctionBeginHot;
7180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7181   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7182   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7183   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7184   PetscCall(DMPlexGetDepth(dm, &depth));
7185   PetscCall(PetscSectionGetNumFields(section, &numFields));
7186   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7187     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7188     PetscFunctionReturn(PETSC_SUCCESS);
7189   }
7190   /* Get points */
7191   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7192   for (clsize = 0, p = 0; p < numPoints; p++) {
7193     PetscInt dof;
7194     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7195     clsize += dof;
7196   }
7197   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7198   /* Get array */
7199   PetscCall(VecGetArray(v, &array));
7200   /* Get values */
7201   if (numFields > 0) {
7202     PetscInt offset = 0, f;
7203     for (f = 0; f < numFields; ++f) {
7204       const PetscInt    **perms = NULL;
7205       const PetscScalar **flips = NULL;
7206 
7207       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7208       switch (mode) {
7209       case INSERT_VALUES:
7210         for (p = 0; p < numPoints; p++) {
7211           const PetscInt     point = points[2 * p];
7212           const PetscInt    *perm  = perms ? perms[p] : NULL;
7213           const PetscScalar *flip  = flips ? flips[p] : NULL;
7214           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7215         }
7216         break;
7217       case INSERT_ALL_VALUES:
7218         for (p = 0; p < numPoints; p++) {
7219           const PetscInt     point = points[2 * p];
7220           const PetscInt    *perm  = perms ? perms[p] : NULL;
7221           const PetscScalar *flip  = flips ? flips[p] : NULL;
7222           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7223         }
7224         break;
7225       case INSERT_BC_VALUES:
7226         for (p = 0; p < numPoints; p++) {
7227           const PetscInt     point = points[2 * p];
7228           const PetscInt    *perm  = perms ? perms[p] : NULL;
7229           const PetscScalar *flip  = flips ? flips[p] : NULL;
7230           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7231         }
7232         break;
7233       case ADD_VALUES:
7234         for (p = 0; p < numPoints; p++) {
7235           const PetscInt     point = points[2 * p];
7236           const PetscInt    *perm  = perms ? perms[p] : NULL;
7237           const PetscScalar *flip  = flips ? flips[p] : NULL;
7238           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7239         }
7240         break;
7241       case ADD_ALL_VALUES:
7242         for (p = 0; p < numPoints; p++) {
7243           const PetscInt     point = points[2 * p];
7244           const PetscInt    *perm  = perms ? perms[p] : NULL;
7245           const PetscScalar *flip  = flips ? flips[p] : NULL;
7246           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7247         }
7248         break;
7249       case ADD_BC_VALUES:
7250         for (p = 0; p < numPoints; p++) {
7251           const PetscInt     point = points[2 * p];
7252           const PetscInt    *perm  = perms ? perms[p] : NULL;
7253           const PetscScalar *flip  = flips ? flips[p] : NULL;
7254           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7255         }
7256         break;
7257       default:
7258         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7259       }
7260       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7261     }
7262   } else {
7263     PetscInt            dof, off;
7264     const PetscInt    **perms = NULL;
7265     const PetscScalar **flips = NULL;
7266 
7267     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7268     switch (mode) {
7269     case INSERT_VALUES:
7270       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7271         const PetscInt     point = points[2 * p];
7272         const PetscInt    *perm  = perms ? perms[p] : NULL;
7273         const PetscScalar *flip  = flips ? flips[p] : NULL;
7274         PetscCall(PetscSectionGetDof(section, point, &dof));
7275         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7276       }
7277       break;
7278     case INSERT_ALL_VALUES:
7279       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7280         const PetscInt     point = points[2 * p];
7281         const PetscInt    *perm  = perms ? perms[p] : NULL;
7282         const PetscScalar *flip  = flips ? flips[p] : NULL;
7283         PetscCall(PetscSectionGetDof(section, point, &dof));
7284         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7285       }
7286       break;
7287     case INSERT_BC_VALUES:
7288       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7289         const PetscInt     point = points[2 * p];
7290         const PetscInt    *perm  = perms ? perms[p] : NULL;
7291         const PetscScalar *flip  = flips ? flips[p] : NULL;
7292         PetscCall(PetscSectionGetDof(section, point, &dof));
7293         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7294       }
7295       break;
7296     case ADD_VALUES:
7297       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
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(PetscSectionGetDof(section, point, &dof));
7302         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7303       }
7304       break;
7305     case ADD_ALL_VALUES:
7306       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7307         const PetscInt     point = points[2 * p];
7308         const PetscInt    *perm  = perms ? perms[p] : NULL;
7309         const PetscScalar *flip  = flips ? flips[p] : NULL;
7310         PetscCall(PetscSectionGetDof(section, point, &dof));
7311         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7312       }
7313       break;
7314     case ADD_BC_VALUES:
7315       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7316         const PetscInt     point = points[2 * p];
7317         const PetscInt    *perm  = perms ? perms[p] : NULL;
7318         const PetscScalar *flip  = flips ? flips[p] : NULL;
7319         PetscCall(PetscSectionGetDof(section, point, &dof));
7320         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7321       }
7322       break;
7323     default:
7324       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7325     }
7326     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7327   }
7328   /* Cleanup points */
7329   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7330   /* Cleanup array */
7331   PetscCall(VecRestoreArray(v, &array));
7332   PetscFunctionReturn(PETSC_SUCCESS);
7333 }
7334 
7335 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7336 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7337 {
7338   PetscFunctionBegin;
7339   *contains = PETSC_TRUE;
7340   if (label) {
7341     PetscInt fdof;
7342 
7343     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7344     if (!*contains) {
7345       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7346       *offset += fdof;
7347       PetscFunctionReturn(PETSC_SUCCESS);
7348     }
7349   }
7350   PetscFunctionReturn(PETSC_SUCCESS);
7351 }
7352 
7353 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7354 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)
7355 {
7356   PetscSection    clSection;
7357   IS              clPoints;
7358   PetscScalar    *array;
7359   PetscInt       *points = NULL;
7360   const PetscInt *clp;
7361   PetscInt        numFields, numPoints, p;
7362   PetscInt        offset = 0, f;
7363 
7364   PetscFunctionBeginHot;
7365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7366   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7367   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7368   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7369   PetscCall(PetscSectionGetNumFields(section, &numFields));
7370   /* Get points */
7371   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7372   /* Get array */
7373   PetscCall(VecGetArray(v, &array));
7374   /* Get values */
7375   for (f = 0; f < numFields; ++f) {
7376     const PetscInt    **perms = NULL;
7377     const PetscScalar **flips = NULL;
7378     PetscBool           contains;
7379 
7380     if (!fieldActive[f]) {
7381       for (p = 0; p < numPoints * 2; p += 2) {
7382         PetscInt fdof;
7383         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7384         offset += fdof;
7385       }
7386       continue;
7387     }
7388     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7389     switch (mode) {
7390     case INSERT_VALUES:
7391       for (p = 0; p < numPoints; p++) {
7392         const PetscInt     point = points[2 * p];
7393         const PetscInt    *perm  = perms ? perms[p] : NULL;
7394         const PetscScalar *flip  = flips ? flips[p] : NULL;
7395         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7396         if (!contains) continue;
7397         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7398       }
7399       break;
7400     case INSERT_ALL_VALUES:
7401       for (p = 0; p < numPoints; p++) {
7402         const PetscInt     point = points[2 * p];
7403         const PetscInt    *perm  = perms ? perms[p] : NULL;
7404         const PetscScalar *flip  = flips ? flips[p] : NULL;
7405         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7406         if (!contains) continue;
7407         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7408       }
7409       break;
7410     case INSERT_BC_VALUES:
7411       for (p = 0; p < numPoints; p++) {
7412         const PetscInt     point = points[2 * p];
7413         const PetscInt    *perm  = perms ? perms[p] : NULL;
7414         const PetscScalar *flip  = flips ? flips[p] : NULL;
7415         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7416         if (!contains) continue;
7417         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7418       }
7419       break;
7420     case ADD_VALUES:
7421       for (p = 0; p < numPoints; p++) {
7422         const PetscInt     point = points[2 * p];
7423         const PetscInt    *perm  = perms ? perms[p] : NULL;
7424         const PetscScalar *flip  = flips ? flips[p] : NULL;
7425         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7426         if (!contains) continue;
7427         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7428       }
7429       break;
7430     case ADD_ALL_VALUES:
7431       for (p = 0; p < numPoints; p++) {
7432         const PetscInt     point = points[2 * p];
7433         const PetscInt    *perm  = perms ? perms[p] : NULL;
7434         const PetscScalar *flip  = flips ? flips[p] : NULL;
7435         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7436         if (!contains) continue;
7437         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7438       }
7439       break;
7440     default:
7441       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7442     }
7443     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7444   }
7445   /* Cleanup points */
7446   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7447   /* Cleanup array */
7448   PetscCall(VecRestoreArray(v, &array));
7449   PetscFunctionReturn(PETSC_SUCCESS);
7450 }
7451 
7452 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7453 {
7454   PetscMPIInt rank;
7455   PetscInt    i, j;
7456 
7457   PetscFunctionBegin;
7458   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7459   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7460   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7461   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7462   numCIndices = numCIndices ? numCIndices : numRIndices;
7463   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7464   for (i = 0; i < numRIndices; i++) {
7465     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7466     for (j = 0; j < numCIndices; j++) {
7467 #if defined(PETSC_USE_COMPLEX)
7468       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7469 #else
7470       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7471 #endif
7472     }
7473     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7474   }
7475   PetscFunctionReturn(PETSC_SUCCESS);
7476 }
7477 
7478 /*
7479   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7480 
7481   Input Parameters:
7482 + section - The section for this data layout
7483 . islocal - Is the section (and thus indices being requested) local or global?
7484 . point   - The point contributing dofs with these indices
7485 . off     - The global offset of this point
7486 . loff    - The local offset of each field
7487 . setBC   - The flag determining whether to include indices of boundary values
7488 . perm    - A permutation of the dofs on this point, or NULL
7489 - indperm - A permutation of the entire indices array, or NULL
7490 
7491   Output Parameter:
7492 . indices - Indices for dofs on this point
7493 
7494   Level: developer
7495 
7496   Note: The indices could be local or global, depending on the value of 'off'.
7497 */
7498 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7499 {
7500   PetscInt        dof;   /* The number of unknowns on this point */
7501   PetscInt        cdof;  /* The number of constraints on this point */
7502   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7503   PetscInt        cind = 0, k;
7504 
7505   PetscFunctionBegin;
7506   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7507   PetscCall(PetscSectionGetDof(section, point, &dof));
7508   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7509   if (!cdof || setBC) {
7510     for (k = 0; k < dof; ++k) {
7511       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7512       const PetscInt ind    = indperm ? indperm[preind] : preind;
7513 
7514       indices[ind] = off + k;
7515     }
7516   } else {
7517     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7518     for (k = 0; k < dof; ++k) {
7519       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7520       const PetscInt ind    = indperm ? indperm[preind] : preind;
7521 
7522       if ((cind < cdof) && (k == cdofs[cind])) {
7523         /* Insert check for returning constrained indices */
7524         indices[ind] = -(off + k + 1);
7525         ++cind;
7526       } else {
7527         indices[ind] = off + k - (islocal ? 0 : cind);
7528       }
7529     }
7530   }
7531   *loff += dof;
7532   PetscFunctionReturn(PETSC_SUCCESS);
7533 }
7534 
7535 /*
7536  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7537 
7538  Input Parameters:
7539 + section - a section (global or local)
7540 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7541 . point - point within section
7542 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7543 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7544 . setBC - identify constrained (boundary condition) points via involution.
7545 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7546 . permsoff - offset
7547 - indperm - index permutation
7548 
7549  Output Parameter:
7550 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7551 . indices - array to hold indices (as defined by section) of each dof associated with point
7552 
7553  Notes:
7554  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7555  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7556  in the local vector.
7557 
7558  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7559  significant).  It is invalid to call with a global section and setBC=true.
7560 
7561  Developer Note:
7562  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7563  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7564  offset could be obtained from the section instead of passing it explicitly as we do now.
7565 
7566  Example:
7567  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7568  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7569  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7570  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.
7571 
7572  Level: developer
7573 */
7574 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[])
7575 {
7576   PetscInt numFields, foff, f;
7577 
7578   PetscFunctionBegin;
7579   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7580   PetscCall(PetscSectionGetNumFields(section, &numFields));
7581   for (f = 0, foff = 0; f < numFields; ++f) {
7582     PetscInt        fdof, cfdof;
7583     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7584     PetscInt        cind = 0, b;
7585     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7586 
7587     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7588     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7589     if (!cfdof || setBC) {
7590       for (b = 0; b < fdof; ++b) {
7591         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7592         const PetscInt ind    = indperm ? indperm[preind] : preind;
7593 
7594         indices[ind] = off + foff + b;
7595       }
7596     } else {
7597       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7598       for (b = 0; b < fdof; ++b) {
7599         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7600         const PetscInt ind    = indperm ? indperm[preind] : preind;
7601 
7602         if ((cind < cfdof) && (b == fcdofs[cind])) {
7603           indices[ind] = -(off + foff + b + 1);
7604           ++cind;
7605         } else {
7606           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7607         }
7608       }
7609     }
7610     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7611     foffs[f] += fdof;
7612   }
7613   PetscFunctionReturn(PETSC_SUCCESS);
7614 }
7615 
7616 /*
7617   This version believes the globalSection offsets for each field, rather than just the point offset
7618 
7619  . foffs - The offset into 'indices' for each field, since it is segregated by field
7620 
7621  Notes:
7622  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7623  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7624 */
7625 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7626 {
7627   PetscInt numFields, foff, f;
7628 
7629   PetscFunctionBegin;
7630   PetscCall(PetscSectionGetNumFields(section, &numFields));
7631   for (f = 0; f < numFields; ++f) {
7632     PetscInt        fdof, cfdof;
7633     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7634     PetscInt        cind = 0, b;
7635     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7636 
7637     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7638     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7639     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7640     if (!cfdof) {
7641       for (b = 0; b < fdof; ++b) {
7642         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7643         const PetscInt ind    = indperm ? indperm[preind] : preind;
7644 
7645         indices[ind] = foff + b;
7646       }
7647     } else {
7648       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7649       for (b = 0; b < fdof; ++b) {
7650         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7651         const PetscInt ind    = indperm ? indperm[preind] : preind;
7652 
7653         if ((cind < cfdof) && (b == fcdofs[cind])) {
7654           indices[ind] = -(foff + b + 1);
7655           ++cind;
7656         } else {
7657           indices[ind] = foff + b - cind;
7658         }
7659       }
7660     }
7661     foffs[f] += fdof;
7662   }
7663   PetscFunctionReturn(PETSC_SUCCESS);
7664 }
7665 
7666 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7667 {
7668   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7669 
7670   PetscFunctionBegin;
7671   PetscCall(PetscSectionGetNumFields(section, &numFields));
7672   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7673   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7674   for (PetscInt p = 0; p < nPoints; p++) {
7675     PetscInt     b       = pnts[2 * p];
7676     PetscInt     bSecDof = 0, bOff;
7677     PetscInt     cSecDof = 0;
7678     PetscSection indices_section;
7679 
7680     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7681     if (!bSecDof) continue;
7682     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7683     indices_section = cSecDof > 0 ? cSec : section;
7684     if (numFields) {
7685       PetscInt fStart[32], fEnd[32];
7686 
7687       fStart[0] = 0;
7688       fEnd[0]   = 0;
7689       for (PetscInt f = 0; f < numFields; f++) {
7690         PetscInt fDof = 0;
7691 
7692         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7693         fStart[f + 1] = fStart[f] + fDof;
7694         fEnd[f + 1]   = fStart[f + 1];
7695       }
7696       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7697       // only apply permutations on one side
7698       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7699       for (PetscInt f = 0; f < numFields; f++) {
7700         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7701       }
7702     } else {
7703       PetscInt bEnd = 0;
7704 
7705       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7706       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7707 
7708       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7709     }
7710   }
7711   PetscFunctionReturn(PETSC_SUCCESS);
7712 }
7713 
7714 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[])
7715 {
7716   Mat             cMat;
7717   PetscSection    aSec, cSec;
7718   IS              aIS;
7719   PetscInt        aStart = -1, aEnd = -1;
7720   PetscInt        sStart = -1, sEnd = -1;
7721   PetscInt        cStart = -1, cEnd = -1;
7722   const PetscInt *anchors;
7723   PetscInt        numFields, p;
7724   PetscInt        newNumPoints = 0, newNumIndices = 0;
7725   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7726   PetscInt        oldOffsets[32];
7727   PetscInt        newOffsets[32];
7728   PetscInt        oldOffsetsCopy[32];
7729   PetscInt        newOffsetsCopy[32];
7730   PetscScalar    *modMat         = NULL;
7731   PetscBool       anyConstrained = PETSC_FALSE;
7732 
7733   PetscFunctionBegin;
7734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7735   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7736   PetscCall(PetscSectionGetNumFields(section, &numFields));
7737 
7738   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7739   /* if there are point-to-point constraints */
7740   if (aSec) {
7741     PetscCall(PetscArrayzero(newOffsets, 32));
7742     PetscCall(PetscArrayzero(oldOffsets, 32));
7743     PetscCall(ISGetIndices(aIS, &anchors));
7744     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7745     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7746     /* figure out how many points are going to be in the new element matrix
7747      * (we allow double counting, because it's all just going to be summed
7748      * into the global matrix anyway) */
7749     for (p = 0; p < 2 * numPoints; p += 2) {
7750       PetscInt b    = points[p];
7751       PetscInt bDof = 0, bSecDof = 0;
7752 
7753       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7754       if (!bSecDof) continue;
7755 
7756       for (PetscInt f = 0; f < numFields; f++) {
7757         PetscInt fDof = 0;
7758 
7759         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7760         oldOffsets[f + 1] += fDof;
7761       }
7762       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7763       if (bDof) {
7764         /* this point is constrained */
7765         /* it is going to be replaced by its anchors */
7766         PetscInt bOff, q;
7767 
7768         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7769         for (q = 0; q < bDof; q++) {
7770           PetscInt a    = anchors[bOff + q];
7771           PetscInt aDof = 0;
7772 
7773           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7774           if (aDof) {
7775             anyConstrained = PETSC_TRUE;
7776             newNumPoints += 1;
7777           }
7778           newNumIndices += aDof;
7779           for (PetscInt f = 0; f < numFields; ++f) {
7780             PetscInt fDof = 0;
7781 
7782             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7783             newOffsets[f + 1] += fDof;
7784           }
7785         }
7786       } else {
7787         /* this point is not constrained */
7788         newNumPoints++;
7789         newNumIndices += bSecDof;
7790         for (PetscInt f = 0; f < numFields; ++f) {
7791           PetscInt fDof;
7792 
7793           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7794           newOffsets[f + 1] += fDof;
7795         }
7796       }
7797     }
7798   }
7799   if (!anyConstrained) {
7800     if (outNumPoints) *outNumPoints = 0;
7801     if (outNumIndices) *outNumIndices = 0;
7802     if (outPoints) *outPoints = NULL;
7803     if (outMat) *outMat = NULL;
7804     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7805     PetscFunctionReturn(PETSC_SUCCESS);
7806   }
7807 
7808   if (outNumPoints) *outNumPoints = newNumPoints;
7809   if (outNumIndices) *outNumIndices = newNumIndices;
7810 
7811   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7812   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7813 
7814   if (!outPoints && !outMat) {
7815     if (offsets) {
7816       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7817     }
7818     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7819     PetscFunctionReturn(PETSC_SUCCESS);
7820   }
7821 
7822   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7823   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7824 
7825   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7826   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7827 
7828   /* output arrays */
7829   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7830   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7831 
7832   // get the new Points
7833   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7834     PetscInt b    = points[2 * p];
7835     PetscInt bDof = 0, bSecDof = 0, bOff;
7836 
7837     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7838     if (!bSecDof) continue;
7839     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7840     if (bDof) {
7841       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7842       for (PetscInt q = 0; q < bDof; q++) {
7843         PetscInt a = anchors[bOff + q], aDof = 0;
7844 
7845         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7846         if (aDof) {
7847           newPoints[2 * newP]     = a;
7848           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7849           newP++;
7850         }
7851       }
7852     } else {
7853       newPoints[2 * newP]     = b;
7854       newPoints[2 * newP + 1] = points[2 * p + 1];
7855       newP++;
7856     }
7857   }
7858 
7859   if (outMat) {
7860     PetscScalar *tmpMat;
7861     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7862     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7863 
7864     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7865     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7866     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7867     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7868 
7869     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7870     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7871 
7872     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7873     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7874 
7875     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7876     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7877     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7878     // for each field, insert the anchor modification into modMat
7879     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7880       PetscInt fStart    = oldOffsets[f];
7881       PetscInt fNewStart = newOffsets[f];
7882       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7883         PetscInt b    = points[2 * p];
7884         PetscInt bDof = 0, bSecDof = 0, bOff;
7885 
7886         if (b >= sStart && b < sEnd) {
7887           if (numFields) {
7888             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7889           } else {
7890             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7891           }
7892         }
7893         if (!bSecDof) continue;
7894         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7895         if (bDof) {
7896           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7897           for (PetscInt q = 0; q < bDof; q++, newP++) {
7898             PetscInt a = anchors[bOff + q], aDof = 0;
7899 
7900             if (a >= sStart && a < sEnd) {
7901               if (numFields) {
7902                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7903               } else {
7904                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7905               }
7906             }
7907             if (aDof) {
7908               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7909               for (PetscInt d = 0; d < bSecDof; d++) {
7910                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7911               }
7912             }
7913             oNew += aDof;
7914           }
7915         } else {
7916           // Insert the identity matrix in this block
7917           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7918           oNew += bSecDof;
7919           newP++;
7920         }
7921         o += bSecDof;
7922       }
7923     }
7924 
7925     *outMat = modMat;
7926 
7927     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7928     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7929     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7930     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7931     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7932   }
7933   PetscCall(ISRestoreIndices(aIS, &anchors));
7934 
7935   /* output */
7936   if (outPoints) {
7937     *outPoints = newPoints;
7938   } else {
7939     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7940   }
7941   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7942   PetscFunctionReturn(PETSC_SUCCESS);
7943 }
7944 
7945 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)
7946 {
7947   PetscScalar *modMat        = NULL;
7948   PetscInt     newNumIndices = -1;
7949 
7950   PetscFunctionBegin;
7951   /* 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.
7952      modMat is that matrix C */
7953   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7954   if (outNumIndices) *outNumIndices = newNumIndices;
7955   if (modMat) {
7956     const PetscScalar *newValues = values;
7957 
7958     if (multiplyRight) {
7959       PetscScalar *newNewValues = NULL;
7960       PetscBLASInt M, N, K;
7961       PetscScalar  a = 1.0, b = 0.0;
7962 
7963       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);
7964 
7965       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7966       PetscCall(PetscBLASIntCast(numRows, &N));
7967       PetscCall(PetscBLASIntCast(numIndices, &K));
7968       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7969       // row-major to column-major conversion, right multiplication becomes left multiplication
7970       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7971       numCols   = newNumIndices;
7972       newValues = newNewValues;
7973     }
7974 
7975     if (multiplyLeft) {
7976       PetscScalar *newNewValues = NULL;
7977       PetscBLASInt M, N, K;
7978       PetscScalar  a = 1.0, b = 0.0;
7979 
7980       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);
7981 
7982       PetscCall(PetscBLASIntCast(numCols, &M));
7983       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7984       PetscCall(PetscBLASIntCast(numIndices, &K));
7985       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7986       // row-major to column-major conversion, left multiplication becomes right multiplication
7987       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7988       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7989       newValues = newNewValues;
7990     }
7991     *outValues = (PetscScalar *)newValues;
7992     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7993   }
7994   PetscFunctionReturn(PETSC_SUCCESS);
7995 }
7996 
7997 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)
7998 {
7999   PetscFunctionBegin;
8000   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8001   PetscFunctionReturn(PETSC_SUCCESS);
8002 }
8003 
8004 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8005 {
8006   /* Closure ordering */
8007   PetscSection    clSection;
8008   IS              clPoints;
8009   const PetscInt *clp;
8010   PetscInt       *points;
8011   PetscInt        Ncl, Ni = 0;
8012 
8013   PetscFunctionBeginHot;
8014   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8015   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8016     PetscInt dof;
8017 
8018     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8019     Ni += dof;
8020   }
8021   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8022   *closureSize = Ni;
8023   PetscFunctionReturn(PETSC_SUCCESS);
8024 }
8025 
8026 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)
8027 {
8028   /* Closure ordering */
8029   PetscSection    clSection;
8030   IS              clPoints;
8031   const PetscInt *clp;
8032   PetscInt       *points;
8033   const PetscInt *clperm = NULL;
8034   /* Dof permutation and sign flips */
8035   const PetscInt    **perms[32] = {NULL};
8036   const PetscScalar **flips[32] = {NULL};
8037   PetscScalar        *valCopy   = NULL;
8038   /* Hanging node constraints */
8039   PetscInt    *pointsC = NULL;
8040   PetscScalar *valuesC = NULL;
8041   PetscInt     NclC, NiC;
8042 
8043   PetscInt *idx;
8044   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8045   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8046   PetscInt  idxStart, idxEnd;
8047   PetscInt  nRows, nCols;
8048 
8049   PetscFunctionBeginHot;
8050   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8051   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8052   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8053   PetscAssertPointer(numRows, 6);
8054   PetscAssertPointer(numCols, 7);
8055   if (indices) PetscAssertPointer(indices, 8);
8056   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8057   if (values) PetscAssertPointer(values, 10);
8058   PetscCall(PetscSectionGetNumFields(section, &Nf));
8059   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8060   PetscCall(PetscArrayzero(offsets, 32));
8061   /* 1) Get points in closure */
8062   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8063   if (useClPerm) {
8064     PetscInt depth, clsize;
8065     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8066     for (clsize = 0, p = 0; p < Ncl; p++) {
8067       PetscInt dof;
8068       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8069       clsize += dof;
8070     }
8071     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8072   }
8073   /* 2) Get number of indices on these points and field offsets from section */
8074   for (p = 0; p < Ncl * 2; p += 2) {
8075     PetscInt dof, fdof;
8076 
8077     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8078     for (f = 0; f < Nf; ++f) {
8079       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8080       offsets[f + 1] += fdof;
8081     }
8082     Ni += dof;
8083   }
8084   if (*numRows == -1) *numRows = Ni;
8085   if (*numCols == -1) *numCols = Ni;
8086   nRows = *numRows;
8087   nCols = *numCols;
8088   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8089   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8090   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8091   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8092   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8093   for (f = 0; f < PetscMax(1, Nf); ++f) {
8094     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8095     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8096     /* may need to apply sign changes to the element matrix */
8097     if (values && flips[f]) {
8098       PetscInt foffset = offsets[f];
8099 
8100       for (p = 0; p < Ncl; ++p) {
8101         PetscInt           pnt  = points[2 * p], fdof;
8102         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8103 
8104         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8105         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8106         if (flip) {
8107           PetscInt i, j, k;
8108 
8109           if (!valCopy) {
8110             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8111             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8112             *values = valCopy;
8113           }
8114           for (i = 0; i < fdof; ++i) {
8115             PetscScalar fval = flip[i];
8116 
8117             if (multiplyRight) {
8118               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8119             }
8120             if (multiplyLeft) {
8121               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8122             }
8123           }
8124         }
8125         foffset += fdof;
8126       }
8127     }
8128   }
8129   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8130   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8131   if (NclC) {
8132     if (multiplyRight) *numCols = NiC;
8133     if (multiplyLeft) *numRows = NiC;
8134     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8135     for (f = 0; f < PetscMax(1, Nf); ++f) {
8136       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8137       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8138     }
8139     for (f = 0; f < PetscMax(1, Nf); ++f) {
8140       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8141       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8142     }
8143     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8144     Ncl    = NclC;
8145     Ni     = NiC;
8146     points = pointsC;
8147     if (values) *values = valuesC;
8148   }
8149   /* 5) Calculate indices */
8150   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8151   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8152   if (Nf) {
8153     PetscInt  idxOff;
8154     PetscBool useFieldOffsets;
8155 
8156     if (outOffsets) {
8157       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8158     }
8159     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8160     if (useFieldOffsets) {
8161       for (p = 0; p < Ncl; ++p) {
8162         const PetscInt pnt = points[p * 2];
8163 
8164         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8165       }
8166     } else {
8167       for (p = 0; p < Ncl; ++p) {
8168         const PetscInt pnt = points[p * 2];
8169 
8170         if (pnt < idxStart || pnt >= idxEnd) continue;
8171         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8172         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8173          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8174          * global section. */
8175         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8176       }
8177     }
8178   } else {
8179     PetscInt off = 0, idxOff;
8180 
8181     for (p = 0; p < Ncl; ++p) {
8182       const PetscInt  pnt  = points[p * 2];
8183       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8184 
8185       if (pnt < idxStart || pnt >= idxEnd) continue;
8186       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8187       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8188        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8189       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8190     }
8191   }
8192   /* 6) Cleanup */
8193   for (f = 0; f < PetscMax(1, Nf); ++f) {
8194     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8195     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8196   }
8197   if (NclC) {
8198     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8199   } else {
8200     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8201   }
8202 
8203   if (indices) *indices = idx;
8204   PetscFunctionReturn(PETSC_SUCCESS);
8205 }
8206 
8207 /*@C
8208   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8209 
8210   Not collective
8211 
8212   Input Parameters:
8213 + dm         - The `DM`
8214 . section    - The `PetscSection` describing the points (a local section)
8215 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8216 . point      - The point defining the closure
8217 - useClPerm  - Use the closure point permutation if available
8218 
8219   Output Parameters:
8220 + numIndices - The number of dof indices in the closure of point with the input sections
8221 . indices    - The dof indices
8222 . outOffsets - Array to write the field offsets into, or `NULL`
8223 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8224 
8225   Level: advanced
8226 
8227   Notes:
8228   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8229 
8230   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8231   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8232   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8233   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8234   indices (with the above semantics) are implied.
8235 
8236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8237           `PetscSection`, `DMGetGlobalSection()`
8238 @*/
8239 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8240 {
8241   PetscInt numRows = -1, numCols = -1;
8242 
8243   PetscFunctionBeginHot;
8244   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8245   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8246   *numIndices = numRows;
8247   PetscFunctionReturn(PETSC_SUCCESS);
8248 }
8249 
8250 /*@C
8251   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8252 
8253   Not collective
8254 
8255   Input Parameters:
8256 + dm         - The `DM`
8257 . section    - The `PetscSection` describing the points (a local section)
8258 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8259 . point      - The point defining the closure
8260 - useClPerm  - Use the closure point permutation if available
8261 
8262   Output Parameters:
8263 + numIndices - The number of dof indices in the closure of point with the input sections
8264 . indices    - The dof indices
8265 . outOffsets - Array to write the field offsets into, or `NULL`
8266 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8267 
8268   Level: advanced
8269 
8270   Notes:
8271   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8272 
8273   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8274   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8275   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8276   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8277   indices (with the above semantics) are implied.
8278 
8279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8280 @*/
8281 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8282 {
8283   PetscFunctionBegin;
8284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8285   PetscAssertPointer(indices, 7);
8286   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8287   PetscFunctionReturn(PETSC_SUCCESS);
8288 }
8289 
8290 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8291 {
8292   DM_Plex           *mesh = (DM_Plex *)dm->data;
8293   PetscInt          *indices;
8294   PetscInt           numIndices;
8295   const PetscScalar *valuesOrig = values;
8296   PetscErrorCode     ierr;
8297 
8298   PetscFunctionBegin;
8299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8300   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8301   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8302   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8303   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8304   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8305 
8306   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8307 
8308   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8309   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8310   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8311   if (ierr) {
8312     PetscMPIInt rank;
8313 
8314     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8315     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8316     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8317     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8318     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8319     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8320   }
8321   if (mesh->printFEM > 1) {
8322     PetscInt i;
8323     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8324     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8325     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8326   }
8327 
8328   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8329   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8330   PetscFunctionReturn(PETSC_SUCCESS);
8331 }
8332 
8333 /*@C
8334   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8335 
8336   Not collective
8337 
8338   Input Parameters:
8339 + dm            - The `DM`
8340 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8341 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8342 . A             - The matrix
8343 . point         - The point in the `DM`
8344 . values        - The array of values
8345 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8346 
8347   Level: intermediate
8348 
8349 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8350 @*/
8351 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8352 {
8353   PetscFunctionBegin;
8354   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8355   PetscFunctionReturn(PETSC_SUCCESS);
8356 }
8357 
8358 /*@C
8359   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8360 
8361   Not collective
8362 
8363   Input Parameters:
8364 + dmRow            - The `DM` for the row fields
8365 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8366 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8367 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8368 . dmCol            - The `DM` for the column fields
8369 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8370 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8371 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8372 . A                - The matrix
8373 . point            - The point in the `DM`
8374 . values           - The array of values
8375 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8376 
8377   Level: intermediate
8378 
8379 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8380 @*/
8381 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)
8382 {
8383   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8384   PetscInt          *indicesRow, *indicesCol;
8385   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8386   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8387 
8388   PetscErrorCode ierr;
8389 
8390   PetscFunctionBegin;
8391   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8392   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8393   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8394   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8395   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8396   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8397   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8398   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8399   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8400   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8401   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8402 
8403   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8404   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8405   valuesV1 = valuesV0;
8406   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8407   valuesV2 = valuesV1;
8408   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8409 
8410   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8411   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8412   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8413   if (ierr) {
8414     PetscMPIInt rank;
8415 
8416     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8417     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8418     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8419     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8420     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8421     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8422     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8423   }
8424 
8425   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8426   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8427   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8428   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8429   PetscFunctionReturn(PETSC_SUCCESS);
8430 }
8431 
8432 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8433 {
8434   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8435   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8436   PetscInt       *cpoints = NULL;
8437   PetscInt       *findices, *cindices;
8438   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8439   PetscInt        foffsets[32], coffsets[32];
8440   DMPolytopeType  ct;
8441   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8442   PetscErrorCode  ierr;
8443 
8444   PetscFunctionBegin;
8445   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8446   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8447   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8448   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8449   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8450   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8451   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8452   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8453   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8454   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8455   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8456   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8457   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8458   PetscCall(PetscArrayzero(foffsets, 32));
8459   PetscCall(PetscArrayzero(coffsets, 32));
8460   /* Column indices */
8461   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8462   maxFPoints = numCPoints;
8463   /* Compress out points not in the section */
8464   /*   TODO: Squeeze out points with 0 dof as well */
8465   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8466   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8467     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8468       cpoints[q * 2]     = cpoints[p];
8469       cpoints[q * 2 + 1] = cpoints[p + 1];
8470       ++q;
8471     }
8472   }
8473   numCPoints = q;
8474   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8475     PetscInt fdof;
8476 
8477     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8478     if (!dof) continue;
8479     for (f = 0; f < numFields; ++f) {
8480       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8481       coffsets[f + 1] += fdof;
8482     }
8483     numCIndices += dof;
8484   }
8485   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8486   /* Row indices */
8487   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8488   {
8489     DMPlexTransform tr;
8490     DMPolytopeType *rct;
8491     PetscInt       *rsize, *rcone, *rornt, Nt;
8492 
8493     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8494     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8495     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8496     numSubcells = rsize[Nt - 1];
8497     PetscCall(DMPlexTransformDestroy(&tr));
8498   }
8499   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8500   for (r = 0, q = 0; r < numSubcells; ++r) {
8501     /* TODO Map from coarse to fine cells */
8502     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8503     /* Compress out points not in the section */
8504     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8505     for (p = 0; p < numFPoints * 2; p += 2) {
8506       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8507         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8508         if (!dof) continue;
8509         for (s = 0; s < q; ++s)
8510           if (fpoints[p] == ftotpoints[s * 2]) break;
8511         if (s < q) continue;
8512         ftotpoints[q * 2]     = fpoints[p];
8513         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8514         ++q;
8515       }
8516     }
8517     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8518   }
8519   numFPoints = q;
8520   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8521     PetscInt fdof;
8522 
8523     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8524     if (!dof) continue;
8525     for (f = 0; f < numFields; ++f) {
8526       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8527       foffsets[f + 1] += fdof;
8528     }
8529     numFIndices += dof;
8530   }
8531   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8532 
8533   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8534   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8535   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8536   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8537   if (numFields) {
8538     const PetscInt **permsF[32] = {NULL};
8539     const PetscInt **permsC[32] = {NULL};
8540 
8541     for (f = 0; f < numFields; f++) {
8542       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8543       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8544     }
8545     for (p = 0; p < numFPoints; p++) {
8546       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8547       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8548     }
8549     for (p = 0; p < numCPoints; p++) {
8550       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8551       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8552     }
8553     for (f = 0; f < numFields; f++) {
8554       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8555       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8556     }
8557   } else {
8558     const PetscInt **permsF = NULL;
8559     const PetscInt **permsC = NULL;
8560 
8561     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8562     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8563     for (p = 0, off = 0; p < numFPoints; p++) {
8564       const PetscInt *perm = permsF ? permsF[p] : NULL;
8565 
8566       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8567       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8568     }
8569     for (p = 0, off = 0; p < numCPoints; p++) {
8570       const PetscInt *perm = permsC ? permsC[p] : NULL;
8571 
8572       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8573       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8574     }
8575     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8576     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8577   }
8578   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8579   /* TODO: flips */
8580   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8581   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8582   if (ierr) {
8583     PetscMPIInt rank;
8584 
8585     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8586     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8587     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8588     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8589     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8590   }
8591   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8592   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8593   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8594   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8595   PetscFunctionReturn(PETSC_SUCCESS);
8596 }
8597 
8598 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8599 {
8600   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8601   PetscInt       *cpoints      = NULL;
8602   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8603   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8604   DMPolytopeType  ct;
8605   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8606 
8607   PetscFunctionBegin;
8608   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8609   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8610   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8611   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8612   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8613   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8614   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8615   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8616   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8617   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8618   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8619   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8620   /* Column indices */
8621   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8622   maxFPoints = numCPoints;
8623   /* Compress out points not in the section */
8624   /*   TODO: Squeeze out points with 0 dof as well */
8625   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8626   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8627     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8628       cpoints[q * 2]     = cpoints[p];
8629       cpoints[q * 2 + 1] = cpoints[p + 1];
8630       ++q;
8631     }
8632   }
8633   numCPoints = q;
8634   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8635     PetscInt fdof;
8636 
8637     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8638     if (!dof) continue;
8639     for (f = 0; f < numFields; ++f) {
8640       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8641       coffsets[f + 1] += fdof;
8642     }
8643     numCIndices += dof;
8644   }
8645   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8646   /* Row indices */
8647   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8648   {
8649     DMPlexTransform tr;
8650     DMPolytopeType *rct;
8651     PetscInt       *rsize, *rcone, *rornt, Nt;
8652 
8653     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8654     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8655     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8656     numSubcells = rsize[Nt - 1];
8657     PetscCall(DMPlexTransformDestroy(&tr));
8658   }
8659   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8660   for (r = 0, q = 0; r < numSubcells; ++r) {
8661     /* TODO Map from coarse to fine cells */
8662     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8663     /* Compress out points not in the section */
8664     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8665     for (p = 0; p < numFPoints * 2; p += 2) {
8666       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8667         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8668         if (!dof) continue;
8669         for (s = 0; s < q; ++s)
8670           if (fpoints[p] == ftotpoints[s * 2]) break;
8671         if (s < q) continue;
8672         ftotpoints[q * 2]     = fpoints[p];
8673         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8674         ++q;
8675       }
8676     }
8677     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8678   }
8679   numFPoints = q;
8680   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8681     PetscInt fdof;
8682 
8683     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8684     if (!dof) continue;
8685     for (f = 0; f < numFields; ++f) {
8686       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8687       foffsets[f + 1] += fdof;
8688     }
8689     numFIndices += dof;
8690   }
8691   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8692 
8693   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8694   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8695   if (numFields) {
8696     const PetscInt **permsF[32] = {NULL};
8697     const PetscInt **permsC[32] = {NULL};
8698 
8699     for (f = 0; f < numFields; f++) {
8700       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8701       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8702     }
8703     for (p = 0; p < numFPoints; p++) {
8704       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8705       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8706     }
8707     for (p = 0; p < numCPoints; p++) {
8708       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8709       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8710     }
8711     for (f = 0; f < numFields; f++) {
8712       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8713       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8714     }
8715   } else {
8716     const PetscInt **permsF = NULL;
8717     const PetscInt **permsC = NULL;
8718 
8719     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8720     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8721     for (p = 0, off = 0; p < numFPoints; p++) {
8722       const PetscInt *perm = permsF ? permsF[p] : NULL;
8723 
8724       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8725       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8726     }
8727     for (p = 0, off = 0; p < numCPoints; p++) {
8728       const PetscInt *perm = permsC ? permsC[p] : NULL;
8729 
8730       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8731       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8732     }
8733     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8734     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8735   }
8736   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8737   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8738   PetscFunctionReturn(PETSC_SUCCESS);
8739 }
8740 
8741 /*@
8742   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8743 
8744   Input Parameter:
8745 . dm - The `DMPLEX` object
8746 
8747   Output Parameter:
8748 . cellHeight - The height of a cell
8749 
8750   Level: developer
8751 
8752 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8753 @*/
8754 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8755 {
8756   DM_Plex *mesh = (DM_Plex *)dm->data;
8757 
8758   PetscFunctionBegin;
8759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8760   PetscAssertPointer(cellHeight, 2);
8761   *cellHeight = mesh->vtkCellHeight;
8762   PetscFunctionReturn(PETSC_SUCCESS);
8763 }
8764 
8765 /*@
8766   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8767 
8768   Input Parameters:
8769 + dm         - The `DMPLEX` object
8770 - cellHeight - The height of a cell
8771 
8772   Level: developer
8773 
8774 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8775 @*/
8776 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8777 {
8778   DM_Plex *mesh = (DM_Plex *)dm->data;
8779 
8780   PetscFunctionBegin;
8781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8782   mesh->vtkCellHeight = cellHeight;
8783   PetscFunctionReturn(PETSC_SUCCESS);
8784 }
8785 
8786 /*@
8787   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8788 
8789   Input Parameters:
8790 + dm - The `DMPLEX` object
8791 - ct - The `DMPolytopeType` of the cell
8792 
8793   Output Parameters:
8794 + start - The first cell of this type, or `NULL`
8795 - end   - The upper bound on this celltype, or `NULL`
8796 
8797   Level: advanced
8798 
8799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8800 @*/
8801 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8802 {
8803   DM_Plex *mesh = (DM_Plex *)dm->data;
8804   DMLabel  label;
8805   PetscInt pStart, pEnd;
8806 
8807   PetscFunctionBegin;
8808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8809   if (start) {
8810     PetscAssertPointer(start, 3);
8811     *start = 0;
8812   }
8813   if (end) {
8814     PetscAssertPointer(end, 4);
8815     *end = 0;
8816   }
8817   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8818   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8819   if (mesh->tr) {
8820     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8821   } else {
8822     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8823     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8824     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8825   }
8826   PetscFunctionReturn(PETSC_SUCCESS);
8827 }
8828 
8829 /*@
8830   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8831 
8832   Input Parameters:
8833 + dm    - The `DMPLEX` object
8834 - depth - The depth for the given point stratum
8835 
8836   Output Parameter:
8837 . gsize - The global number of points in the stratum
8838 
8839   Level: advanced
8840 
8841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8842 @*/
8843 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8844 {
8845   PetscSF         sf;
8846   const PetscInt *leaves;
8847   PetscInt        Nl, loc, start, end, lsize = 0;
8848 
8849   PetscFunctionBegin;
8850   PetscCall(DMGetPointSF(dm, &sf));
8851   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8852   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8853   for (PetscInt p = start; p < end; ++p) {
8854     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8855     if (loc < 0) ++lsize;
8856   }
8857   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8858   PetscFunctionReturn(PETSC_SUCCESS);
8859 }
8860 
8861 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8862 {
8863   PetscSection section, globalSection;
8864   PetscInt    *numbers, p;
8865 
8866   PetscFunctionBegin;
8867   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8868   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8869   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8870   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8871   PetscCall(PetscSectionSetUp(section));
8872   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8873   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8874   for (p = pStart; p < pEnd; ++p) {
8875     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8876     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8877     else numbers[p - pStart] += shift;
8878   }
8879   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8880   if (globalSize) {
8881     PetscLayout layout;
8882     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8883     PetscCall(PetscLayoutGetSize(layout, globalSize));
8884     PetscCall(PetscLayoutDestroy(&layout));
8885   }
8886   PetscCall(PetscSectionDestroy(&section));
8887   PetscCall(PetscSectionDestroy(&globalSection));
8888   PetscFunctionReturn(PETSC_SUCCESS);
8889 }
8890 
8891 /*@
8892   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8893 
8894   Input Parameters:
8895 + dm         - The `DMPLEX` object
8896 - includeAll - Whether to include all cells, or just the simplex and box cells
8897 
8898   Output Parameter:
8899 . globalCellNumbers - Global cell numbers for all cells on this process
8900 
8901   Level: developer
8902 
8903 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8904 @*/
8905 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8906 {
8907   PetscInt cellHeight, cStart, cEnd;
8908 
8909   PetscFunctionBegin;
8910   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8911   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8912   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8913   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8914   PetscFunctionReturn(PETSC_SUCCESS);
8915 }
8916 
8917 /*@
8918   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8919 
8920   Input Parameter:
8921 . dm - The `DMPLEX` object
8922 
8923   Output Parameter:
8924 . globalCellNumbers - Global cell numbers for all cells on this process
8925 
8926   Level: developer
8927 
8928 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8929 @*/
8930 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8931 {
8932   DM_Plex *mesh = (DM_Plex *)dm->data;
8933 
8934   PetscFunctionBegin;
8935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8936   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8937   *globalCellNumbers = mesh->globalCellNumbers;
8938   PetscFunctionReturn(PETSC_SUCCESS);
8939 }
8940 
8941 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8942 {
8943   PetscInt vStart, vEnd;
8944 
8945   PetscFunctionBegin;
8946   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8947   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8948   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8949   PetscFunctionReturn(PETSC_SUCCESS);
8950 }
8951 
8952 /*@
8953   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8954 
8955   Input Parameter:
8956 . dm - The `DMPLEX` object
8957 
8958   Output Parameter:
8959 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8960 
8961   Level: developer
8962 
8963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8964 @*/
8965 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8966 {
8967   DM_Plex *mesh = (DM_Plex *)dm->data;
8968 
8969   PetscFunctionBegin;
8970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8971   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8972   *globalVertexNumbers = mesh->globalVertexNumbers;
8973   PetscFunctionReturn(PETSC_SUCCESS);
8974 }
8975 
8976 /*@
8977   DMPlexCreatePointNumbering - Create a global numbering for all points.
8978 
8979   Collective
8980 
8981   Input Parameter:
8982 . dm - The `DMPLEX` object
8983 
8984   Output Parameter:
8985 . globalPointNumbers - Global numbers for all points on this process
8986 
8987   Level: developer
8988 
8989   Notes:
8990   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8991   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8992   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8993   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8994 
8995   The partitioned mesh is
8996   ```
8997   (2)--0--(3)--1--(4)    (1)--0--(2)
8998   ```
8999   and its global numbering is
9000   ```
9001   (3)--0--(4)--1--(5)--2--(6)
9002   ```
9003   Then the global numbering is provided as
9004   ```
9005   [0] Number of indices in set 5
9006   [0] 0 0
9007   [0] 1 1
9008   [0] 2 3
9009   [0] 3 4
9010   [0] 4 -6
9011   [1] Number of indices in set 3
9012   [1] 0 2
9013   [1] 1 5
9014   [1] 2 6
9015   ```
9016 
9017 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9018 @*/
9019 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9020 {
9021   IS        nums[4];
9022   PetscInt  depths[4], gdepths[4], starts[4];
9023   PetscInt  depth, d, shift = 0;
9024   PetscBool empty = PETSC_FALSE;
9025 
9026   PetscFunctionBegin;
9027   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9028   PetscCall(DMPlexGetDepth(dm, &depth));
9029   // For unstratified meshes use dim instead of depth
9030   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9031   // If any stratum is empty, we must mark all empty
9032   for (d = 0; d <= depth; ++d) {
9033     PetscInt end;
9034 
9035     depths[d] = depth - d;
9036     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9037     if (!(starts[d] - end)) empty = PETSC_TRUE;
9038   }
9039   if (empty)
9040     for (d = 0; d <= depth; ++d) {
9041       depths[d] = -1;
9042       starts[d] = -1;
9043     }
9044   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9045   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9046   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]);
9047   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9048   for (d = 0; d <= depth; ++d) {
9049     PetscInt pStart, pEnd, gsize;
9050 
9051     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9052     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9053     shift += gsize;
9054   }
9055   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9056   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9057   PetscFunctionReturn(PETSC_SUCCESS);
9058 }
9059 
9060 /*@
9061   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9062 
9063   Collective
9064 
9065   Input Parameter:
9066 . dm - The `DMPLEX` object
9067 
9068   Output Parameter:
9069 . globalEdgeNumbers - Global numbers for all edges on this process
9070 
9071   Level: developer
9072 
9073   Notes:
9074   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).
9075 
9076 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9077 @*/
9078 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9079 {
9080   PetscSF  sf;
9081   PetscInt eStart, eEnd;
9082 
9083   PetscFunctionBegin;
9084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9085   PetscCall(DMGetPointSF(dm, &sf));
9086   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9087   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9088   PetscFunctionReturn(PETSC_SUCCESS);
9089 }
9090 
9091 /*@
9092   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9093 
9094   Input Parameter:
9095 . dm - The `DMPLEX` object
9096 
9097   Output Parameter:
9098 . ranks - The rank field
9099 
9100   Options Database Key:
9101 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9102 
9103   Level: intermediate
9104 
9105 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9106 @*/
9107 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9108 {
9109   DM             rdm;
9110   PetscFE        fe;
9111   PetscScalar   *r;
9112   PetscMPIInt    rank;
9113   DMPolytopeType ct;
9114   PetscInt       dim, cStart, cEnd, c;
9115   PetscBool      simplex;
9116 
9117   PetscFunctionBeginUser;
9118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9119   PetscAssertPointer(ranks, 2);
9120   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9121   PetscCall(DMClone(dm, &rdm));
9122   PetscCall(DMGetDimension(rdm, &dim));
9123   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9124   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9125   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9126   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9127   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9128   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9129   PetscCall(PetscFEDestroy(&fe));
9130   PetscCall(DMCreateDS(rdm));
9131   PetscCall(DMCreateGlobalVector(rdm, ranks));
9132   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9133   PetscCall(VecGetArray(*ranks, &r));
9134   for (c = cStart; c < cEnd; ++c) {
9135     PetscScalar *lr;
9136 
9137     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9138     if (lr) *lr = rank;
9139   }
9140   PetscCall(VecRestoreArray(*ranks, &r));
9141   PetscCall(DMDestroy(&rdm));
9142   PetscFunctionReturn(PETSC_SUCCESS);
9143 }
9144 
9145 /*@
9146   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9147 
9148   Input Parameters:
9149 + dm    - The `DMPLEX`
9150 - label - The `DMLabel`
9151 
9152   Output Parameter:
9153 . val - The label value field
9154 
9155   Options Database Key:
9156 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9157 
9158   Level: intermediate
9159 
9160 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9161 @*/
9162 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9163 {
9164   DM             rdm, plex;
9165   Vec            lval;
9166   PetscSection   section;
9167   PetscFE        fe;
9168   PetscScalar   *v;
9169   PetscInt       dim, pStart, pEnd, p, cStart;
9170   DMPolytopeType ct;
9171   char           name[PETSC_MAX_PATH_LEN];
9172   const char    *lname, *prefix;
9173 
9174   PetscFunctionBeginUser;
9175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9176   PetscAssertPointer(label, 2);
9177   PetscAssertPointer(val, 3);
9178   PetscCall(DMClone(dm, &rdm));
9179   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9180   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9181   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9182   PetscCall(DMDestroy(&plex));
9183   PetscCall(DMGetDimension(rdm, &dim));
9184   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9185   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9186   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9187   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9188   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9189   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9190   PetscCall(PetscFEDestroy(&fe));
9191   PetscCall(DMCreateDS(rdm));
9192   PetscCall(DMCreateGlobalVector(rdm, val));
9193   PetscCall(DMCreateLocalVector(rdm, &lval));
9194   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9195   PetscCall(DMGetLocalSection(rdm, &section));
9196   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9197   PetscCall(VecGetArray(lval, &v));
9198   for (p = pStart; p < pEnd; ++p) {
9199     PetscInt cval, dof, off;
9200 
9201     PetscCall(PetscSectionGetDof(section, p, &dof));
9202     if (!dof) continue;
9203     PetscCall(DMLabelGetValue(label, p, &cval));
9204     PetscCall(PetscSectionGetOffset(section, p, &off));
9205     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9206   }
9207   PetscCall(VecRestoreArray(lval, &v));
9208   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9209   PetscCall(VecDestroy(&lval));
9210   PetscCall(DMDestroy(&rdm));
9211   PetscFunctionReturn(PETSC_SUCCESS);
9212 }
9213 
9214 /*@
9215   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9216 
9217   Input Parameter:
9218 . dm - The `DMPLEX` object
9219 
9220   Level: developer
9221 
9222   Notes:
9223   This is a useful diagnostic when creating meshes programmatically.
9224 
9225   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9226 
9227 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9228 @*/
9229 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9230 {
9231   PetscSection    coneSection, supportSection;
9232   const PetscInt *cone, *support;
9233   PetscInt        coneSize, c, supportSize, s;
9234   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9235   PetscBool       storagecheck = PETSC_TRUE;
9236 
9237   PetscFunctionBegin;
9238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9239   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9240   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9241   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9242   /* Check that point p is found in the support of its cone points, and vice versa */
9243   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9244   for (p = pStart; p < pEnd; ++p) {
9245     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9246     PetscCall(DMPlexGetCone(dm, p, &cone));
9247     for (c = 0; c < coneSize; ++c) {
9248       PetscBool dup = PETSC_FALSE;
9249       PetscInt  d;
9250       for (d = c - 1; d >= 0; --d) {
9251         if (cone[c] == cone[d]) {
9252           dup = PETSC_TRUE;
9253           break;
9254         }
9255       }
9256       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9257       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9258       for (s = 0; s < supportSize; ++s) {
9259         if (support[s] == p) break;
9260       }
9261       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9262         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9263         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9264         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9265         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9266         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9267         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9268         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]);
9269         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9270       }
9271     }
9272     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9273     if (p != pp) {
9274       storagecheck = PETSC_FALSE;
9275       continue;
9276     }
9277     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9278     PetscCall(DMPlexGetSupport(dm, p, &support));
9279     for (s = 0; s < supportSize; ++s) {
9280       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9281       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9282       for (c = 0; c < coneSize; ++c) {
9283         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9284         if (cone[c] != pp) {
9285           c = 0;
9286           break;
9287         }
9288         if (cone[c] == p) break;
9289       }
9290       if (c >= coneSize) {
9291         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9292         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9293         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9294         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9295         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9296         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9297         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9298       }
9299     }
9300   }
9301   if (storagecheck) {
9302     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9303     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9304     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9305   }
9306   PetscFunctionReturn(PETSC_SUCCESS);
9307 }
9308 
9309 /*
9310   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.
9311 */
9312 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9313 {
9314   DMPolytopeType  cct;
9315   PetscInt        ptpoints[4];
9316   const PetscInt *cone, *ccone, *ptcone;
9317   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9318 
9319   PetscFunctionBegin;
9320   *unsplit = 0;
9321   switch (ct) {
9322   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9323     ptpoints[npt++] = c;
9324     break;
9325   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9326     PetscCall(DMPlexGetCone(dm, c, &cone));
9327     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9328     for (cp = 0; cp < coneSize; ++cp) {
9329       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9330       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9331     }
9332     break;
9333   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9334   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9335     PetscCall(DMPlexGetCone(dm, c, &cone));
9336     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9337     for (cp = 0; cp < coneSize; ++cp) {
9338       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9339       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9340       for (ccp = 0; ccp < cconeSize; ++ccp) {
9341         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9342         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9343           PetscInt p;
9344           for (p = 0; p < npt; ++p)
9345             if (ptpoints[p] == ccone[ccp]) break;
9346           if (p == npt) ptpoints[npt++] = ccone[ccp];
9347         }
9348       }
9349     }
9350     break;
9351   default:
9352     break;
9353   }
9354   for (pt = 0; pt < npt; ++pt) {
9355     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9356     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9357   }
9358   PetscFunctionReturn(PETSC_SUCCESS);
9359 }
9360 
9361 /*@
9362   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9363 
9364   Input Parameters:
9365 + dm         - The `DMPLEX` object
9366 - cellHeight - Normally 0
9367 
9368   Level: developer
9369 
9370   Notes:
9371   This is a useful diagnostic when creating meshes programmatically.
9372   Currently applicable only to homogeneous simplex or tensor meshes.
9373 
9374   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9375 
9376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9377 @*/
9378 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9379 {
9380   DMPlexInterpolatedFlag interp;
9381   DMPolytopeType         ct;
9382   PetscInt               vStart, vEnd, cStart, cEnd, c;
9383 
9384   PetscFunctionBegin;
9385   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9386   PetscCall(DMPlexIsInterpolated(dm, &interp));
9387   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9388   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9389   for (c = cStart; c < cEnd; ++c) {
9390     PetscInt *closure = NULL;
9391     PetscInt  coneSize, closureSize, cl, Nv = 0;
9392 
9393     PetscCall(DMPlexGetCellType(dm, c, &ct));
9394     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9395     if (interp == DMPLEX_INTERPOLATED_FULL) {
9396       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9397       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));
9398     }
9399     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9400     for (cl = 0; cl < closureSize * 2; cl += 2) {
9401       const PetscInt p = closure[cl];
9402       if ((p >= vStart) && (p < vEnd)) ++Nv;
9403     }
9404     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9405     /* Special Case: Tensor faces with identified vertices */
9406     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9407       PetscInt unsplit;
9408 
9409       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9410       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9411     }
9412     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));
9413   }
9414   PetscFunctionReturn(PETSC_SUCCESS);
9415 }
9416 
9417 /*@
9418   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9419 
9420   Collective
9421 
9422   Input Parameters:
9423 + dm         - The `DMPLEX` object
9424 - cellHeight - Normally 0
9425 
9426   Level: developer
9427 
9428   Notes:
9429   This is a useful diagnostic when creating meshes programmatically.
9430   This routine is only relevant for meshes that are fully interpolated across all ranks.
9431   It will error out if a partially interpolated mesh is given on some rank.
9432   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9433 
9434   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9435 
9436 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9437 @*/
9438 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9439 {
9440   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9441   DMPlexInterpolatedFlag interpEnum;
9442 
9443   PetscFunctionBegin;
9444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9445   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9446   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9447   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9448     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9449     PetscFunctionReturn(PETSC_SUCCESS);
9450   }
9451 
9452   PetscCall(DMGetDimension(dm, &dim));
9453   PetscCall(DMPlexGetDepth(dm, &depth));
9454   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9455   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9456     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9457     for (c = cStart; c < cEnd; ++c) {
9458       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9459       const DMPolytopeType *faceTypes;
9460       DMPolytopeType        ct;
9461       PetscInt              numFaces, coneSize, f;
9462       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9463 
9464       PetscCall(DMPlexGetCellType(dm, c, &ct));
9465       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9466       if (unsplit) continue;
9467       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9468       PetscCall(DMPlexGetCone(dm, c, &cone));
9469       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9470       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9471       for (cl = 0; cl < closureSize * 2; cl += 2) {
9472         const PetscInt p = closure[cl];
9473         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9474       }
9475       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9476       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);
9477       for (f = 0; f < numFaces; ++f) {
9478         DMPolytopeType fct;
9479         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9480 
9481         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9482         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9483         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9484           const PetscInt p = fclosure[cl];
9485           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9486         }
9487         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]);
9488         for (v = 0; v < fnumCorners; ++v) {
9489           if (fclosure[v] != faces[fOff + v]) {
9490             PetscInt v1;
9491 
9492             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9493             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9494             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9495             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9496             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9497             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]);
9498           }
9499         }
9500         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9501         fOff += faceSizes[f];
9502       }
9503       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9504       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9505     }
9506   }
9507   PetscFunctionReturn(PETSC_SUCCESS);
9508 }
9509 
9510 /*@
9511   DMPlexCheckGeometry - Check the geometry of mesh cells
9512 
9513   Input Parameter:
9514 . dm - The `DMPLEX` object
9515 
9516   Level: developer
9517 
9518   Notes:
9519   This is a useful diagnostic when creating meshes programmatically.
9520 
9521   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9522 
9523 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9524 @*/
9525 PetscErrorCode DMPlexCheckGeometry(DM dm)
9526 {
9527   Vec       coordinates;
9528   PetscReal detJ, J[9], refVol = 1.0;
9529   PetscReal vol;
9530   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9531 
9532   PetscFunctionBegin;
9533   PetscCall(DMGetDimension(dm, &dim));
9534   PetscCall(DMGetCoordinateDim(dm, &dE));
9535   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9536   PetscCall(DMPlexGetDepth(dm, &depth));
9537   for (d = 0; d < dim; ++d) refVol *= 2.0;
9538   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9539   /* Make sure local coordinates are created, because that step is collective */
9540   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9541   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9542   for (c = cStart; c < cEnd; ++c) {
9543     DMPolytopeType ct;
9544     PetscInt       unsplit;
9545     PetscBool      ignoreZeroVol = PETSC_FALSE;
9546 
9547     PetscCall(DMPlexGetCellType(dm, c, &ct));
9548     switch (ct) {
9549     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9550     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9551     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9552       ignoreZeroVol = PETSC_TRUE;
9553       break;
9554     default:
9555       break;
9556     }
9557     switch (ct) {
9558     case DM_POLYTOPE_TRI_PRISM:
9559     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9560     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9561     case DM_POLYTOPE_PYRAMID:
9562       continue;
9563     default:
9564       break;
9565     }
9566     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9567     if (unsplit) continue;
9568     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9569     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);
9570     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9571     /* This should work with periodicity since DG coordinates should be used */
9572     if (depth > 1) {
9573       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9574       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);
9575       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9576     }
9577   }
9578   PetscFunctionReturn(PETSC_SUCCESS);
9579 }
9580 
9581 /*@
9582   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9583 
9584   Collective
9585 
9586   Input Parameters:
9587 + dm              - The `DMPLEX` object
9588 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9589 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9590 
9591   Level: developer
9592 
9593   Notes:
9594   This is mainly intended for debugging/testing purposes.
9595 
9596   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9597 
9598   Extra roots can come from periodic cuts, where additional points appear on the boundary
9599 
9600 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9601 @*/
9602 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9603 {
9604   PetscInt           l, nleaves, nroots, overlap;
9605   const PetscInt    *locals;
9606   const PetscSFNode *remotes;
9607   PetscBool          distributed;
9608   MPI_Comm           comm;
9609   PetscMPIInt        rank;
9610 
9611   PetscFunctionBegin;
9612   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9613   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9614   else pointSF = dm->sf;
9615   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9616   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9617   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9618   {
9619     PetscMPIInt mpiFlag;
9620 
9621     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9622     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9623   }
9624   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9625   PetscCall(DMPlexIsDistributed(dm, &distributed));
9626   if (!distributed) {
9627     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);
9628     PetscFunctionReturn(PETSC_SUCCESS);
9629   }
9630   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);
9631   PetscCall(DMPlexGetOverlap(dm, &overlap));
9632 
9633   /* Check SF graph is compatible with DMPlex chart */
9634   {
9635     PetscInt pStart, pEnd, maxLeaf;
9636 
9637     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9638     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9639     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9640     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9641   }
9642 
9643   /* Check there are no cells in interface */
9644   if (!overlap) {
9645     PetscInt cellHeight, cStart, cEnd;
9646 
9647     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9648     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9649     for (l = 0; l < nleaves; ++l) {
9650       const PetscInt point = locals ? locals[l] : l;
9651 
9652       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9653     }
9654   }
9655 
9656   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9657   {
9658     const PetscInt *rootdegree;
9659 
9660     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9661     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9662     for (l = 0; l < nleaves; ++l) {
9663       const PetscInt  point = locals ? locals[l] : l;
9664       const PetscInt *cone;
9665       PetscInt        coneSize, c, idx;
9666 
9667       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9668       PetscCall(DMPlexGetCone(dm, point, &cone));
9669       for (c = 0; c < coneSize; ++c) {
9670         if (!rootdegree[cone[c]]) {
9671           if (locals) {
9672             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9673           } else {
9674             idx = (cone[c] < nleaves) ? cone[c] : -1;
9675           }
9676           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9677         }
9678       }
9679     }
9680   }
9681   PetscFunctionReturn(PETSC_SUCCESS);
9682 }
9683 
9684 /*@
9685   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9686 
9687   Collective
9688 
9689   Input Parameter:
9690 . dm - The `DMPLEX` object
9691 
9692   Level: developer
9693 
9694   Notes:
9695   This is mainly intended for debugging/testing purposes.
9696 
9697   Other cell types which are disconnected would be caught by the symmetry and face checks.
9698 
9699   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9700 
9701 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9702 @*/
9703 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9704 {
9705   PetscInt pStart, pEnd, vStart, vEnd;
9706 
9707   PetscFunctionBegin;
9708   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9709   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9710   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9711   for (PetscInt v = vStart; v < vEnd; ++v) {
9712     PetscInt suppSize;
9713 
9714     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9715     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9716   }
9717   PetscFunctionReturn(PETSC_SUCCESS);
9718 }
9719 
9720 /*@
9721   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9722 
9723   Input Parameter:
9724 . dm - The `DMPLEX` object
9725 
9726   Level: developer
9727 
9728   Notes:
9729   This is a useful diagnostic when creating meshes programmatically.
9730 
9731   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9732 
9733   Currently does not include `DMPlexCheckCellShape()`.
9734 
9735 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9736 @*/
9737 PetscErrorCode DMPlexCheck(DM dm)
9738 {
9739   PetscInt cellHeight;
9740 
9741   PetscFunctionBegin;
9742   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9743   PetscCall(DMPlexCheckSymmetry(dm));
9744   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9745   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9746   PetscCall(DMPlexCheckGeometry(dm));
9747   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9748   PetscCall(DMPlexCheckInterfaceCones(dm));
9749   PetscCall(DMPlexCheckOrphanVertices(dm));
9750   PetscFunctionReturn(PETSC_SUCCESS);
9751 }
9752 
9753 typedef struct cell_stats {
9754   PetscReal min, max, sum, squaresum;
9755   PetscInt  count;
9756 } cell_stats_t;
9757 
9758 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9759 {
9760   PetscInt i, N = *len;
9761 
9762   for (i = 0; i < N; i++) {
9763     cell_stats_t *A = (cell_stats_t *)a;
9764     cell_stats_t *B = (cell_stats_t *)b;
9765 
9766     B->min = PetscMin(A->min, B->min);
9767     B->max = PetscMax(A->max, B->max);
9768     B->sum += A->sum;
9769     B->squaresum += A->squaresum;
9770     B->count += A->count;
9771   }
9772 }
9773 
9774 /*@
9775   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9776 
9777   Collective
9778 
9779   Input Parameters:
9780 + dm        - The `DMPLEX` object
9781 . output    - If true, statistics will be displayed on `stdout`
9782 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9783 
9784   Level: developer
9785 
9786   Notes:
9787   This is mainly intended for debugging/testing purposes.
9788 
9789   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9790 
9791 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9792 @*/
9793 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9794 {
9795   DM           dmCoarse;
9796   cell_stats_t stats, globalStats;
9797   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9798   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9799   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9800   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9801   PetscMPIInt  rank, size;
9802 
9803   PetscFunctionBegin;
9804   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9805   stats.min = PETSC_MAX_REAL;
9806   stats.max = PETSC_MIN_REAL;
9807   stats.sum = stats.squaresum = 0.;
9808   stats.count                 = 0;
9809 
9810   PetscCallMPI(MPI_Comm_size(comm, &size));
9811   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9812   PetscCall(DMGetCoordinateDim(dm, &cdim));
9813   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9814   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9815   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9816   for (c = cStart; c < cEnd; c++) {
9817     PetscInt  i;
9818     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9819 
9820     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9821     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9822     for (i = 0; i < PetscSqr(cdim); ++i) {
9823       frobJ += J[i] * J[i];
9824       frobInvJ += invJ[i] * invJ[i];
9825     }
9826     cond2 = frobJ * frobInvJ;
9827     cond  = PetscSqrtReal(cond2);
9828 
9829     stats.min = PetscMin(stats.min, cond);
9830     stats.max = PetscMax(stats.max, cond);
9831     stats.sum += cond;
9832     stats.squaresum += cond2;
9833     stats.count++;
9834     if (output && cond > limit) {
9835       PetscSection coordSection;
9836       Vec          coordsLocal;
9837       PetscScalar *coords = NULL;
9838       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9839 
9840       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9841       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9842       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9843       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9844       for (i = 0; i < Nv / cdim; ++i) {
9845         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9846         for (d = 0; d < cdim; ++d) {
9847           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9848           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9849         }
9850         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9851       }
9852       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9853       for (cl = 0; cl < clSize * 2; cl += 2) {
9854         const PetscInt edge = closure[cl];
9855 
9856         if ((edge >= eStart) && (edge < eEnd)) {
9857           PetscReal len;
9858 
9859           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9860           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9861         }
9862       }
9863       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9864       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9865     }
9866   }
9867   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9868 
9869   if (size > 1) {
9870     PetscMPIInt  blockLengths[2] = {4, 1};
9871     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9872     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9873     MPI_Op       statReduce;
9874 
9875     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9876     PetscCallMPI(MPI_Type_commit(&statType));
9877     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9878     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9879     PetscCallMPI(MPI_Op_free(&statReduce));
9880     PetscCallMPI(MPI_Type_free(&statType));
9881   } else {
9882     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9883   }
9884   if (rank == 0) {
9885     count = globalStats.count;
9886     min   = globalStats.min;
9887     max   = globalStats.max;
9888     mean  = globalStats.sum / globalStats.count;
9889     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9890   }
9891 
9892   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));
9893   PetscCall(PetscFree2(J, invJ));
9894 
9895   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9896   if (dmCoarse) {
9897     PetscBool isplex;
9898 
9899     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9900     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9901   }
9902   PetscFunctionReturn(PETSC_SUCCESS);
9903 }
9904 
9905 /*@
9906   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9907   orthogonal quality below given tolerance.
9908 
9909   Collective
9910 
9911   Input Parameters:
9912 + dm   - The `DMPLEX` object
9913 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9914 - atol - [0, 1] Absolute tolerance for tagging cells.
9915 
9916   Output Parameters:
9917 + OrthQual      - `Vec` containing orthogonal quality per cell
9918 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9919 
9920   Options Database Keys:
9921 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9922 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9923 
9924   Level: intermediate
9925 
9926   Notes:
9927   Orthogonal quality is given by the following formula\:
9928 
9929   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9930 
9931   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
9932   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9933   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9934   calculating the cosine of the angle between these vectors.
9935 
9936   Orthogonal quality ranges from 1 (best) to 0 (worst).
9937 
9938   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9939   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9940 
9941   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9942 
9943 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9944 @*/
9945 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9946 {
9947   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9948   PetscInt              *idx;
9949   PetscScalar           *oqVals;
9950   const PetscScalar     *cellGeomArr, *faceGeomArr;
9951   PetscReal             *ci, *fi, *Ai;
9952   MPI_Comm               comm;
9953   Vec                    cellgeom, facegeom;
9954   DM                     dmFace, dmCell;
9955   IS                     glob;
9956   ISLocalToGlobalMapping ltog;
9957   PetscViewer            vwr;
9958 
9959   PetscFunctionBegin;
9960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9961   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9962   PetscAssertPointer(OrthQual, 4);
9963   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9964   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9965   PetscCall(DMGetDimension(dm, &nc));
9966   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9967   {
9968     DMPlexInterpolatedFlag interpFlag;
9969 
9970     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9971     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9972       PetscMPIInt rank;
9973 
9974       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9975       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9976     }
9977   }
9978   if (OrthQualLabel) {
9979     PetscAssertPointer(OrthQualLabel, 5);
9980     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9981     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9982   } else {
9983     *OrthQualLabel = NULL;
9984   }
9985   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9986   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9987   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9988   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9989   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9990   PetscCall(VecCreate(comm, OrthQual));
9991   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9992   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9993   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9994   PetscCall(VecSetUp(*OrthQual));
9995   PetscCall(ISDestroy(&glob));
9996   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9997   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9998   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9999   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10000   PetscCall(VecGetDM(cellgeom, &dmCell));
10001   PetscCall(VecGetDM(facegeom, &dmFace));
10002   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10003   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10004     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10005     PetscInt         cellarr[2], *adj = NULL;
10006     PetscScalar     *cArr, *fArr;
10007     PetscReal        minvalc = 1.0, minvalf = 1.0;
10008     PetscFVCellGeom *cg;
10009 
10010     idx[cellIter] = cell - cStart;
10011     cellarr[0]    = cell;
10012     /* Make indexing into cellGeom easier */
10013     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10014     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10015     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10016     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10017     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10018       PetscInt         i;
10019       const PetscInt   neigh  = adj[cellneigh];
10020       PetscReal        normci = 0, normfi = 0, normai = 0;
10021       PetscFVCellGeom *cgneigh;
10022       PetscFVFaceGeom *fg;
10023 
10024       /* Don't count ourselves in the neighbor list */
10025       if (neigh == cell) continue;
10026       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10027       cellarr[1] = neigh;
10028       {
10029         PetscInt        numcovpts;
10030         const PetscInt *covpts;
10031 
10032         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10033         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10034         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10035       }
10036 
10037       /* Compute c_i, f_i and their norms */
10038       for (i = 0; i < nc; i++) {
10039         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10040         fi[i] = fg->centroid[i] - cg->centroid[i];
10041         Ai[i] = fg->normal[i];
10042         normci += PetscPowReal(ci[i], 2);
10043         normfi += PetscPowReal(fi[i], 2);
10044         normai += PetscPowReal(Ai[i], 2);
10045       }
10046       normci = PetscSqrtReal(normci);
10047       normfi = PetscSqrtReal(normfi);
10048       normai = PetscSqrtReal(normai);
10049 
10050       /* Normalize and compute for each face-cell-normal pair */
10051       for (i = 0; i < nc; i++) {
10052         ci[i] = ci[i] / normci;
10053         fi[i] = fi[i] / normfi;
10054         Ai[i] = Ai[i] / normai;
10055         /* PetscAbs because I don't know if normals are guaranteed to point out */
10056         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10057         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10058       }
10059       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10060       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10061     }
10062     PetscCall(PetscFree(adj));
10063     PetscCall(PetscFree2(cArr, fArr));
10064     /* Defer to cell if they're equal */
10065     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10066     if (OrthQualLabel) {
10067       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10068     }
10069   }
10070   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10071   PetscCall(VecAssemblyBegin(*OrthQual));
10072   PetscCall(VecAssemblyEnd(*OrthQual));
10073   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10074   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10075   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10076   if (OrthQualLabel) {
10077     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10078   }
10079   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10080   PetscCall(PetscViewerDestroy(&vwr));
10081   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10082   PetscFunctionReturn(PETSC_SUCCESS);
10083 }
10084 
10085 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10086  * interpolator construction */
10087 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10088 {
10089   PetscSection section, newSection, gsection;
10090   PetscSF      sf;
10091   PetscBool    hasConstraints, ghasConstraints;
10092 
10093   PetscFunctionBegin;
10094   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10095   PetscAssertPointer(odm, 2);
10096   PetscCall(DMGetLocalSection(dm, &section));
10097   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10098   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10099   if (!ghasConstraints) {
10100     PetscCall(PetscObjectReference((PetscObject)dm));
10101     *odm = dm;
10102     PetscFunctionReturn(PETSC_SUCCESS);
10103   }
10104   PetscCall(DMClone(dm, odm));
10105   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10106   PetscCall(DMGetLocalSection(*odm, &newSection));
10107   PetscCall(DMGetPointSF(*odm, &sf));
10108   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10109   PetscCall(DMSetGlobalSection(*odm, gsection));
10110   PetscCall(PetscSectionDestroy(&gsection));
10111   PetscFunctionReturn(PETSC_SUCCESS);
10112 }
10113 
10114 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10115 {
10116   DM        dmco, dmfo;
10117   Mat       interpo;
10118   Vec       rscale;
10119   Vec       cglobalo, clocal;
10120   Vec       fglobal, fglobalo, flocal;
10121   PetscBool regular;
10122 
10123   PetscFunctionBegin;
10124   PetscCall(DMGetFullDM(dmc, &dmco));
10125   PetscCall(DMGetFullDM(dmf, &dmfo));
10126   PetscCall(DMSetCoarseDM(dmfo, dmco));
10127   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10128   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10129   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10130   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10131   PetscCall(DMCreateLocalVector(dmc, &clocal));
10132   PetscCall(VecSet(cglobalo, 0.));
10133   PetscCall(VecSet(clocal, 0.));
10134   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10135   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10136   PetscCall(DMCreateLocalVector(dmf, &flocal));
10137   PetscCall(VecSet(fglobal, 0.));
10138   PetscCall(VecSet(fglobalo, 0.));
10139   PetscCall(VecSet(flocal, 0.));
10140   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10141   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10142   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10143   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10144   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10145   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10146   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10147   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10148   *shift = fglobal;
10149   PetscCall(VecDestroy(&flocal));
10150   PetscCall(VecDestroy(&fglobalo));
10151   PetscCall(VecDestroy(&clocal));
10152   PetscCall(VecDestroy(&cglobalo));
10153   PetscCall(VecDestroy(&rscale));
10154   PetscCall(MatDestroy(&interpo));
10155   PetscCall(DMDestroy(&dmfo));
10156   PetscCall(DMDestroy(&dmco));
10157   PetscFunctionReturn(PETSC_SUCCESS);
10158 }
10159 
10160 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10161 {
10162   PetscObject shifto;
10163   Vec         shift;
10164 
10165   PetscFunctionBegin;
10166   if (!interp) {
10167     Vec rscale;
10168 
10169     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10170     PetscCall(VecDestroy(&rscale));
10171   } else {
10172     PetscCall(PetscObjectReference((PetscObject)interp));
10173   }
10174   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10175   if (!shifto) {
10176     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10177     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10178     shifto = (PetscObject)shift;
10179     PetscCall(VecDestroy(&shift));
10180   }
10181   shift = (Vec)shifto;
10182   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10183   PetscCall(VecAXPY(fineSol, 1.0, shift));
10184   PetscCall(MatDestroy(&interp));
10185   PetscFunctionReturn(PETSC_SUCCESS);
10186 }
10187 
10188 /* Pointwise interpolation
10189      Just code FEM for now
10190      u^f = I u^c
10191      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10192      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10193      I_{ij} = psi^f_i phi^c_j
10194 */
10195 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10196 {
10197   PetscSection gsc, gsf;
10198   PetscInt     m, n;
10199   void        *ctx;
10200   DM           cdm;
10201   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10202 
10203   PetscFunctionBegin;
10204   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10205   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10206   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10207   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10208 
10209   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10210   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10211   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10212   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10213   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10214 
10215   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10216   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10217   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10218   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10219   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10220   if (scaling) {
10221     /* Use naive scaling */
10222     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10223   }
10224   PetscFunctionReturn(PETSC_SUCCESS);
10225 }
10226 
10227 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10228 {
10229   VecScatter ctx;
10230 
10231   PetscFunctionBegin;
10232   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10233   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10234   PetscCall(VecScatterDestroy(&ctx));
10235   PetscFunctionReturn(PETSC_SUCCESS);
10236 }
10237 
10238 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[])
10239 {
10240   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10241   const PetscInt Nc = uOff[f + 1] - uOff[f];
10242   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10243 }
10244 
10245 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10246 {
10247   DM           dmc;
10248   PetscDS      ds;
10249   Vec          ones, locmass;
10250   IS           cellIS;
10251   PetscFormKey key;
10252   PetscInt     depth;
10253 
10254   PetscFunctionBegin;
10255   PetscCall(DMClone(dm, &dmc));
10256   PetscCall(DMCopyDisc(dm, dmc));
10257   PetscCall(DMGetDS(dmc, &ds));
10258   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10259   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10260   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10261   else PetscCall(DMGetLocalVector(dm, &locmass));
10262   PetscCall(DMGetLocalVector(dm, &ones));
10263   PetscCall(DMPlexGetDepth(dm, &depth));
10264   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10265   PetscCall(VecSet(locmass, 0.0));
10266   PetscCall(VecSet(ones, 1.0));
10267   key.label = NULL;
10268   key.value = 0;
10269   key.field = 0;
10270   key.part  = 0;
10271   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10272   PetscCall(ISDestroy(&cellIS));
10273   if (mass) {
10274     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10275     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10276   }
10277   PetscCall(DMRestoreLocalVector(dm, &ones));
10278   if (lmass) *lmass = locmass;
10279   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10280   PetscCall(DMDestroy(&dmc));
10281   PetscFunctionReturn(PETSC_SUCCESS);
10282 }
10283 
10284 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10285 {
10286   PetscSection gsc, gsf;
10287   PetscInt     m, n;
10288   void        *ctx;
10289   DM           cdm;
10290   PetscBool    regular;
10291 
10292   PetscFunctionBegin;
10293   if (dmFine == dmCoarse) {
10294     DM            dmc;
10295     PetscDS       ds;
10296     PetscWeakForm wf;
10297     Vec           u;
10298     IS            cellIS;
10299     PetscFormKey  key;
10300     PetscInt      depth;
10301 
10302     PetscCall(DMClone(dmFine, &dmc));
10303     PetscCall(DMCopyDisc(dmFine, dmc));
10304     PetscCall(DMGetDS(dmc, &ds));
10305     PetscCall(PetscDSGetWeakForm(ds, &wf));
10306     PetscCall(PetscWeakFormClear(wf));
10307     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10308     PetscCall(DMCreateMatrix(dmc, mass));
10309     PetscCall(DMGetLocalVector(dmc, &u));
10310     PetscCall(DMPlexGetDepth(dmc, &depth));
10311     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10312     PetscCall(MatZeroEntries(*mass));
10313     key.label = NULL;
10314     key.value = 0;
10315     key.field = 0;
10316     key.part  = 0;
10317     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10318     PetscCall(ISDestroy(&cellIS));
10319     PetscCall(DMRestoreLocalVector(dmc, &u));
10320     PetscCall(DMDestroy(&dmc));
10321   } else {
10322     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10323     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10324     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10325     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10326 
10327     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10328     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10329     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10330     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10331 
10332     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10333     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10334     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10335     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10336   }
10337   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10338   PetscFunctionReturn(PETSC_SUCCESS);
10339 }
10340 
10341 /*@
10342   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10343 
10344   Input Parameter:
10345 . dm - The `DMPLEX` object
10346 
10347   Output Parameter:
10348 . regular - The flag
10349 
10350   Level: intermediate
10351 
10352 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10353 @*/
10354 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10355 {
10356   PetscFunctionBegin;
10357   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10358   PetscAssertPointer(regular, 2);
10359   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10360   PetscFunctionReturn(PETSC_SUCCESS);
10361 }
10362 
10363 /*@
10364   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10365 
10366   Input Parameters:
10367 + dm      - The `DMPLEX` object
10368 - regular - The flag
10369 
10370   Level: intermediate
10371 
10372 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10373 @*/
10374 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10375 {
10376   PetscFunctionBegin;
10377   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10378   ((DM_Plex *)dm->data)->regularRefinement = regular;
10379   PetscFunctionReturn(PETSC_SUCCESS);
10380 }
10381 
10382 /*@
10383   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10384   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10385 
10386   Not Collective
10387 
10388   Input Parameter:
10389 . dm - The `DMPLEX` object
10390 
10391   Output Parameters:
10392 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10393 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10394 
10395   Level: intermediate
10396 
10397 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10398 @*/
10399 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10400 {
10401   DM_Plex *plex = (DM_Plex *)dm->data;
10402 
10403   PetscFunctionBegin;
10404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10405   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10406   if (anchorSection) *anchorSection = plex->anchorSection;
10407   if (anchorIS) *anchorIS = plex->anchorIS;
10408   PetscFunctionReturn(PETSC_SUCCESS);
10409 }
10410 
10411 /*@
10412   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10413 
10414   Collective
10415 
10416   Input Parameters:
10417 + dm            - The `DMPLEX` object
10418 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10419                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10420 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10421 
10422   Level: intermediate
10423 
10424   Notes:
10425   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10426   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10427   combination of other points' degrees of freedom.
10428 
10429   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10430   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10431 
10432   The reference counts of `anchorSection` and `anchorIS` are incremented.
10433 
10434 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10435 @*/
10436 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10437 {
10438   DM_Plex    *plex = (DM_Plex *)dm->data;
10439   PetscMPIInt result;
10440 
10441   PetscFunctionBegin;
10442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10443   if (anchorSection) {
10444     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10445     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10446     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10447   }
10448   if (anchorIS) {
10449     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10450     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10451     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10452   }
10453 
10454   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10455   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10456   plex->anchorSection = anchorSection;
10457 
10458   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10459   PetscCall(ISDestroy(&plex->anchorIS));
10460   plex->anchorIS = anchorIS;
10461 
10462   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10463     PetscInt        size, a, pStart, pEnd;
10464     const PetscInt *anchors;
10465 
10466     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10467     PetscCall(ISGetLocalSize(anchorIS, &size));
10468     PetscCall(ISGetIndices(anchorIS, &anchors));
10469     for (a = 0; a < size; a++) {
10470       PetscInt p;
10471 
10472       p = anchors[a];
10473       if (p >= pStart && p < pEnd) {
10474         PetscInt dof;
10475 
10476         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10477         if (dof) {
10478           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10479           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10480         }
10481       }
10482     }
10483     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10484   }
10485   /* reset the generic constraints */
10486   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10487   PetscFunctionReturn(PETSC_SUCCESS);
10488 }
10489 
10490 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10491 {
10492   PetscSection anchorSection;
10493   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10494 
10495   PetscFunctionBegin;
10496   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10497   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10498   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10499   PetscCall(PetscSectionGetNumFields(section, &numFields));
10500   if (numFields) {
10501     PetscInt f;
10502     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10503 
10504     for (f = 0; f < numFields; f++) {
10505       PetscInt numComp;
10506 
10507       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10508       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10509     }
10510   }
10511   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10512   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10513   pStart = PetscMax(pStart, sStart);
10514   pEnd   = PetscMin(pEnd, sEnd);
10515   pEnd   = PetscMax(pStart, pEnd);
10516   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10517   for (p = pStart; p < pEnd; p++) {
10518     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10519     if (dof) {
10520       PetscCall(PetscSectionGetDof(section, p, &dof));
10521       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10522       for (f = 0; f < numFields; f++) {
10523         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10524         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10525       }
10526     }
10527   }
10528   PetscCall(PetscSectionSetUp(*cSec));
10529   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10530   PetscFunctionReturn(PETSC_SUCCESS);
10531 }
10532 
10533 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10534 {
10535   PetscSection    aSec;
10536   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10537   const PetscInt *anchors;
10538   PetscInt        numFields, f;
10539   IS              aIS;
10540   MatType         mtype;
10541   PetscBool       iscuda, iskokkos;
10542 
10543   PetscFunctionBegin;
10544   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10545   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10546   PetscCall(PetscSectionGetStorageSize(section, &n));
10547   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10548   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10549   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10550   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10551   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10552   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10553   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10554   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10555   else mtype = MATSEQAIJ;
10556   PetscCall(MatSetType(*cMat, mtype));
10557   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10558   PetscCall(ISGetIndices(aIS, &anchors));
10559   /* cSec will be a subset of aSec and section */
10560   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10561   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10562   PetscCall(PetscMalloc1(m + 1, &i));
10563   i[0] = 0;
10564   PetscCall(PetscSectionGetNumFields(section, &numFields));
10565   for (p = pStart; p < pEnd; p++) {
10566     PetscInt rDof, rOff, r;
10567 
10568     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10569     if (!rDof) continue;
10570     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10571     if (numFields) {
10572       for (f = 0; f < numFields; f++) {
10573         annz = 0;
10574         for (r = 0; r < rDof; r++) {
10575           a = anchors[rOff + r];
10576           if (a < sStart || a >= sEnd) continue;
10577           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10578           annz += aDof;
10579         }
10580         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10581         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10582         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10583       }
10584     } else {
10585       annz = 0;
10586       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10587       for (q = 0; q < dof; q++) {
10588         a = anchors[rOff + q];
10589         if (a < sStart || a >= sEnd) continue;
10590         PetscCall(PetscSectionGetDof(section, a, &aDof));
10591         annz += aDof;
10592       }
10593       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10594       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10595       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10596     }
10597   }
10598   nnz = i[m];
10599   PetscCall(PetscMalloc1(nnz, &j));
10600   offset = 0;
10601   for (p = pStart; p < pEnd; p++) {
10602     if (numFields) {
10603       for (f = 0; f < numFields; f++) {
10604         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10605         for (q = 0; q < dof; q++) {
10606           PetscInt rDof, rOff, r;
10607           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10608           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10609           for (r = 0; r < rDof; r++) {
10610             PetscInt s;
10611 
10612             a = anchors[rOff + r];
10613             if (a < sStart || a >= sEnd) continue;
10614             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10615             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10616             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10617           }
10618         }
10619       }
10620     } else {
10621       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10622       for (q = 0; q < dof; q++) {
10623         PetscInt rDof, rOff, r;
10624         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10625         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10626         for (r = 0; r < rDof; r++) {
10627           PetscInt s;
10628 
10629           a = anchors[rOff + r];
10630           if (a < sStart || a >= sEnd) continue;
10631           PetscCall(PetscSectionGetDof(section, a, &aDof));
10632           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10633           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10634         }
10635       }
10636     }
10637   }
10638   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10639   PetscCall(PetscFree(i));
10640   PetscCall(PetscFree(j));
10641   PetscCall(ISRestoreIndices(aIS, &anchors));
10642   PetscFunctionReturn(PETSC_SUCCESS);
10643 }
10644 
10645 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10646 {
10647   DM_Plex     *plex = (DM_Plex *)dm->data;
10648   PetscSection anchorSection, section, cSec;
10649   Mat          cMat;
10650 
10651   PetscFunctionBegin;
10652   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10653   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10654   if (anchorSection) {
10655     PetscInt Nf;
10656 
10657     PetscCall(DMGetLocalSection(dm, &section));
10658     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10659     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10660     PetscCall(DMGetNumFields(dm, &Nf));
10661     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10662     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10663     PetscCall(PetscSectionDestroy(&cSec));
10664     PetscCall(MatDestroy(&cMat));
10665   }
10666   PetscFunctionReturn(PETSC_SUCCESS);
10667 }
10668 
10669 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10670 {
10671   IS           subis;
10672   PetscSection section, subsection;
10673 
10674   PetscFunctionBegin;
10675   PetscCall(DMGetLocalSection(dm, &section));
10676   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10677   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10678   /* Create subdomain */
10679   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10680   /* Create submodel */
10681   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10682   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10683   PetscCall(DMSetLocalSection(*subdm, subsection));
10684   PetscCall(PetscSectionDestroy(&subsection));
10685   PetscCall(DMCopyDisc(dm, *subdm));
10686   /* Create map from submodel to global model */
10687   if (is) {
10688     PetscSection    sectionGlobal, subsectionGlobal;
10689     IS              spIS;
10690     const PetscInt *spmap;
10691     PetscInt       *subIndices;
10692     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10693     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10694 
10695     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10696     PetscCall(ISGetIndices(spIS, &spmap));
10697     PetscCall(PetscSectionGetNumFields(section, &Nf));
10698     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10699     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10700     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10701     for (p = pStart; p < pEnd; ++p) {
10702       PetscInt gdof, pSubSize = 0;
10703 
10704       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10705       if (gdof > 0) {
10706         for (f = 0; f < Nf; ++f) {
10707           PetscInt fdof, fcdof;
10708 
10709           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10710           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10711           pSubSize += fdof - fcdof;
10712         }
10713         subSize += pSubSize;
10714         if (pSubSize) {
10715           if (bs < 0) {
10716             bs = pSubSize;
10717           } else if (bs != pSubSize) {
10718             /* Layout does not admit a pointwise block size */
10719             bs = 1;
10720           }
10721         }
10722       }
10723     }
10724     /* Must have same blocksize on all procs (some might have no points) */
10725     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10726     bsLocal[1] = bs;
10727     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10728     if (bsMinMax[0] != bsMinMax[1]) {
10729       bs = 1;
10730     } else {
10731       bs = bsMinMax[0];
10732     }
10733     PetscCall(PetscMalloc1(subSize, &subIndices));
10734     for (p = pStart; p < pEnd; ++p) {
10735       PetscInt gdof, goff;
10736 
10737       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10738       if (gdof > 0) {
10739         const PetscInt point = spmap[p];
10740 
10741         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10742         for (f = 0; f < Nf; ++f) {
10743           PetscInt fdof, fcdof, fc, f2, poff = 0;
10744 
10745           /* Can get rid of this loop by storing field information in the global section */
10746           for (f2 = 0; f2 < f; ++f2) {
10747             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10748             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10749             poff += fdof - fcdof;
10750           }
10751           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10752           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10753           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10754         }
10755       }
10756     }
10757     PetscCall(ISRestoreIndices(spIS, &spmap));
10758     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10759     if (bs > 1) {
10760       /* We need to check that the block size does not come from non-contiguous fields */
10761       PetscInt i, j, set = 1;
10762       for (i = 0; i < subSize; i += bs) {
10763         for (j = 0; j < bs; ++j) {
10764           if (subIndices[i + j] != subIndices[i] + j) {
10765             set = 0;
10766             break;
10767           }
10768         }
10769       }
10770       if (set) PetscCall(ISSetBlockSize(*is, bs));
10771     }
10772     /* Attach nullspace */
10773     for (f = 0; f < Nf; ++f) {
10774       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10775       if ((*subdm)->nullspaceConstructors[f]) break;
10776     }
10777     if (f < Nf) {
10778       MatNullSpace nullSpace;
10779       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10780 
10781       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10782       PetscCall(MatNullSpaceDestroy(&nullSpace));
10783     }
10784   }
10785   PetscFunctionReturn(PETSC_SUCCESS);
10786 }
10787 
10788 /*@
10789   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10790 
10791   Input Parameters:
10792 + dm    - The `DM`
10793 - dummy - unused argument
10794 
10795   Options Database Key:
10796 . -dm_plex_monitor_throughput - Activate the monitor
10797 
10798   Level: developer
10799 
10800 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10801 @*/
10802 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10803 {
10804   PetscLogHandler default_handler;
10805 
10806   PetscFunctionBegin;
10807   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10808   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10809   if (default_handler) {
10810     PetscLogEvent      event;
10811     PetscEventPerfInfo eventInfo;
10812     PetscLogDouble     cellRate, flopRate;
10813     PetscInt           cStart, cEnd, Nf, N;
10814     const char        *name;
10815 
10816     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10817     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10818     PetscCall(DMGetNumFields(dm, &Nf));
10819     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10820     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10821     N        = (cEnd - cStart) * Nf * eventInfo.count;
10822     flopRate = eventInfo.flops / eventInfo.time;
10823     cellRate = N / eventInfo.time;
10824     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));
10825   } else {
10826     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.");
10827   }
10828   PetscFunctionReturn(PETSC_SUCCESS);
10829 }
10830